Integração Zero-Touch.
Esqueça configurações manuais complexas. O BortolaIA-X foi desenhado para ser plug-and-play: instale o EA e o licenciamento se resolve sozinho.
Guia de Ativação Profissional
Otimize seu tempo com o provisionamento automático.
Aquisição & Checkout
Escolha seu plano. Sua chave EA-XXXX é gerada instantaneamente após a aprovação do pagamento no Mercado Pago.
Autorização WebRequest
Autorize o domínio da BortolaIA-X no seu MetaTrader 5 para permitir a comunicação segura via TLS.
Instalação & Hardware
Baixe o MQ5, instale na pasta Experts e insira sua chave. O HWID é vinculado automaticamente no primeiro tick.
Vínculo Inteligente
O sistema detecta seu número de conta e vincula à licença de forma transparente, sem formulários chatos.
Heartbeat de Segurança
O EA revalida a licença a cada hora. Se detectar acesso não autorizado, a operação é bloqueada imediatamente.
Dashboard em Tempo Real
Acompanhe métricas avançadas, curva de patrimônio e telemetria de qualquer lugar do mundo via web.
1. Autorização de Tráfego MT5
Configure seu terminal para aceitar conexões criptografadas.
O MetaTrader 5 bloqueia conexões externas por padrão. Você precisa autorizar explicitamente o domínio da BortolaIA-X para permitir a validação em tempo real.
Abra o MT5 e vá em Ferramentas > Opções (Ctrl+O).
Acesse a aba Expert Advisors.
Marque 'Permitir WebRequest para as URLs listadas'.
Clique no botão '+' e adicione a URL abaixo:
https://bortolaia.com.br2. Segurança & HWID
Proteção de ponta a ponta vinculada ao seu hardware.
Utilizamos um protocolo híbrido de autenticação. Sua licença não é apenas uma senha, é um vínculo digital único com sua estação de trading.
Provisionamento Imediato
Aprovação automática em milissegundos após o checkout.
Auto-Vínculo de HWID
Seu Hardware ID é registrado no primeiro acesso, sem intervenção humana.
Criptografia de Ponta
Toda telemetria é assinada digitalmente via HMAC-SHA256.
Gestão Dinâmica
Troca de conta MT5 permitida a cada 15 dias direto no painel.
Protocolo de Validação
Endpoint Público v2POST /api/public/license/validate
key=EA-XXXX-XXXX-XXXX&account=51204488&broker=BortolaIA
valid: trueAcesso total autorizado.not_foundChave inexistente no sistema.expiredPlano vencido. Requer renovação.account_limitLimite de terminais atingido.suspendedBloqueio administrativo ativo.device_mismatchTentativa de uso em outro hardware.3. Módulo MQL5 Native
Framework robusto para validação e telemetria.
//+------------------------------------------------------------------+
//| BortolaIA_X_License.mqh|
//| Validacao de licenca individual por usuario / conta MT5 |
//+------------------------------------------------------------------+
#property strict
//--- Cada usuario cola AQUI a chave gerada no painel (formato EA-XXXX-XXXX-XXXX)
input string InpLicenseKey = ""; // Sua chave de licenca
input int InpSRNiveisDist = 2500; // SR Niveis — distancia em pontos
input int InpParcialPercent = 50; // Parcial — percentual do lote a fechar
input ENUM_TIMEFRAMES InpBotaoTF1 = PERIOD_M1; // Botao TF1
input ENUM_TIMEFRAMES InpBotaoTF2 = PERIOD_M5; // Botao TF2
input ENUM_TIMEFRAMES InpBotaoTF3 = PERIOD_M15; // Botao TF3
//--- Constantes internas (nao aparecem nos inputs do EA)
const bool InpBloquearSeInvalida = true;
string BASE_URL = "https://bortolaia.com.br";
//+------------------------------------------------------------------+
//| Gera o HMAC-SHA256 para seguranca da transmissao |
//+------------------------------------------------------------------+
void StringToUtf8NoNull(const string value, char &buffer[])
{
StringToCharArray(value, buffer, 0, WHOLE_ARRAY, CP_UTF8);
while(ArraySize(buffer) > 0 && buffer[ArraySize(buffer)-1] == 0)
ArrayResize(buffer, ArraySize(buffer)-1);
}
string Sha256Hex(const string value)
{
char source[];
uchar data[], key[], hash[];
StringToUtf8NoNull(value, source);
ArrayResize(data, ArraySize(source));
for(int i=0; i<ArraySize(source); i++) data[i] = (uchar)source[i];
ArrayResize(key, 0);
int res = CryptEncode(CRYPT_HASH_SHA256, data, key, hash);
if(res <= 0) return "";
string hex = "";
for(int i=0; i<ArraySize(hash); i++) hex += StringFormat("%02x", hash[i]);
return hex;
}
string ApiSignature(const string body)
{
return Sha256Hex(body + InpLicenseKey);
}
//--- Estado global da licenca
bool g_licenca_ok = false;
string g_plano = "";
int g_dias_restantes = -1;
datetime g_ultima_checagem = 0;
//+------------------------------------------------------------------+
//| Extrai um valor simples de um JSON plano |
//+------------------------------------------------------------------+
string JsonGet(const string json, const string campo)
{
string alvo = "\"" + campo + "\":";
int p = StringFind(json, alvo);
if(p < 0) return "";
p += StringLen(alvo);
while(p < StringLen(json) && (StringGetCharacter(json, p) == ' ' ||
StringGetCharacter(json, p) == '"')) p++;
int fim = p;
while(fim < StringLen(json))
{
ushort c = StringGetCharacter(json, fim);
if(c == ',' || c == '}' || c == '"') break;
fim++;
}
return StringSubstr(json, p, fim - p);
}
//+------------------------------------------------------------------+
//| Valida a licenca do usuario contra o servidor BortolaIA-X |
//| Vincula automaticamente a conta MT5 atual a chave informada. |
//+------------------------------------------------------------------+
bool ValidarLicenca()
{
if(StringLen(InpLicenseKey) < 15)
{
Print("[BortolaIA-X] ERRO: Chave de licenca invalida ou ausente. Insira sua chave nos parametros (ex: EA-XXXX-XXXX-XXXX).");
return false;
}
long conta = AccountInfoInteger(ACCOUNT_LOGIN);
string broker = AccountInfoString(ACCOUNT_COMPANY);
// Device Fingerprint para seguranca extra (bloqueio por hardware)
string hardware = TerminalInfoString(TERMINAL_CPU_NAME) + TerminalInfoString(TERMINAL_PATH);
// Payload canonico: evita problemas de aspas, barras e no WebRequest do MT5.
string body = "key=" + InpLicenseKey
+ "&account=" + (string)conta
+ "&broker=" + broker
+ "&hardware_id=" + hardware;
char post[], result[];
string result_headers;
string signature = ApiSignature(body);
string headers = "Content-Type: application/x-www-form-urlencoded\r\n" +
"X-Signature: " + signature + "\r\n";
StringToUtf8NoNull(body, post);
ResetLastError();
int code = WebRequest("POST", BASE_URL + "/api/public/license/validate",
headers, 8000, post, result, result_headers);
if(code == -1)
{
int err = GetLastError();
Print("[BortolaIA-X] WebRequest falhou (erro ", err, ").");
Print("Adicione ", BASE_URL, " em Ferramentas > Opcoes > Expert Advisors.");
return false;
}
string resposta = CharArrayToString(result, 0, WHOLE_ARRAY, CP_UTF8);
if(JsonGet(resposta, "valid") != "true")
{
Print("[BortolaIA-X] Licenca recusada: ", JsonGet(resposta, "message"),
" (codigo: ", JsonGet(resposta, "code"), ")");
return false;
}
g_plano = JsonGet(resposta, "plan");
g_dias_restantes = (int)StringToInteger(JsonGet(resposta, "days_left"));
g_ultima_checagem = TimeCurrent();
Print("[BortolaIA-X] Licenca ATIVA | Conta: ", conta,
" | Plano: ", g_plano,
" | Dias restantes: ", g_dias_restantes);
return true;
}
//+------------------------------------------------------------------+
//| OnInit — bloqueia o EA se a licenca nao for valida |
//+------------------------------------------------------------------+
int OnInit()
{
g_licenca_ok = ValidarLicenca();
if(!g_licenca_ok && InpBloquearSeInvalida)
{
Print("[BortolaIA-X] Licenca invalida para esta conta. EA nao sera iniciado.");
return(INIT_FAILED);
}
EventSetTimer(3600); // revalida a cada 1 hora
return(INIT_SUCCEEDED);
}
//+------------------------------------------------------------------+
//| Revalidacao periodica (expiracao / bloqueio remoto) |
//+------------------------------------------------------------------+
void OnTimer()
{
if(!ValidarLicenca() && InpBloquearSeInvalida)
{
Print("[BortolaIA-X] Licenca revogada ou expirada. Encerrando o EA.");
ExpertRemove();
}
}
void OnDeinit(const int reason) {
EventKillTimer();
}
Sincronização de Dados
//--- Telemetria (Protocolo de Envio)
//+------------------------------------------------------------------+
//| Envio de telemetria de operacoes para o painel BortolaIA-X |
//| Chame apos o fechamento de cada posicao. |
//+------------------------------------------------------------------+
string IsoTime(datetime t)
{
return TimeToString(t, TIME_DATE) + "T" +
TimeToString(t, TIME_SECONDS) + "Z";
}
void EnviarTelemetria(long ticket, string symbol, string tipo, double volume,
double preco_abertura, double preco_fechamento,
double lucro, datetime abertura, datetime fechamento)
{
long conta = AccountInfoInteger(ACCOUNT_LOGIN);
string body = "key=" + InpLicenseKey
+ "&account=" + (string)conta
+ "&ticket=" + (string)ticket
+ "&symbol=" + symbol
+ "&type=" + tipo
+ "&volume=" + DoubleToString(volume, 2)
+ "&open_price=" + DoubleToString(preco_abertura, 5)
+ "&close_price=" + DoubleToString(preco_fechamento, 5)
+ "&profit=" + DoubleToString(lucro, 2)
+ "&open_time=" + IsoTime(abertura)
+ "&close_time=" + IsoTime(fechamento);
char post[], result[];
string result_headers;
string signature = ApiSignature(body);
string headers = "Content-Type: application/x-www-form-urlencoded\r\n" +
"X-Signature: " + signature + "\r\n";
StringToUtf8NoNull(body, post);
int code = WebRequest("POST", BASE_URL + "/api/public/telemetry",
headers, 5000, post, result, result_headers);
if(code != 200)
Print("[BortolaIA-X] Falha ao sincronizar operacao ", ticket, " (HTTP ", code, ")");
}
//--- Sincronização (Lógica de Varredura de Histórico)
void SincronizarHistorico()
{
datetime de = (g_ultimo_envio == 0) ? TimeCurrent() - 86400 : g_ultimo_envio;
if(!HistorySelect(de, TimeCurrent())) return;
int total = HistoryDealsTotal();
for(int i = 0; i < total; i++)
{
ulong deal = HistoryDealGetTicket(i);
if(deal == 0) continue;
if(HistoryDealGetInteger(deal, DEAL_ENTRY) != DEAL_ENTRY_OUT) continue;
datetime fechamento = (datetime)HistoryDealGetInteger(deal, DEAL_TIME);
if(fechamento <= g_ultimo_envio) continue;
string simbolo = HistoryDealGetString(deal, DEAL_SYMBOL);
long tipo = HistoryDealGetInteger(deal, DEAL_TYPE);
double volume = HistoryDealGetDouble(deal, DEAL_VOLUME);
double preco = HistoryDealGetDouble(deal, DEAL_PRICE);
double lucro = HistoryDealGetDouble(deal, DEAL_PROFIT);
string lado = (tipo == DEAL_TYPE_SELL) ? "buy" : "sell";
EnviarTelemetria(deal, simbolo, lado, volume, preco, preco,
lucro, fechamento, fechamento);
}
g_ultimo_envio = TimeCurrent();
}4. Expert Advisor Full Stack
O BortolaIA-X completo, integrado e pronto para o gráfico.
//+------------------------------------------------------------------+
//| BortolaIA_X.mq5 |
//| Expert Advisor com licenciamento individual + telemetria |
//| Copie este arquivo para MQL5/Experts e compile no MetaEditor. |
//+------------------------------------------------------------------+
#property copyright "BortolaIA-X"
#property link "https://bortolaia.com.br"
#property version "1.00"
#property strict
//==================== PARAMETROS DE ENTRADA ========================
input string InpLicenseKey = ""; // Sua chave de licenca
input int InpSRNiveisDist = 2500; // SR Niveis — distancia em pontos
input int InpParcialPercent = 50; // Parcial — percentual do lote a fechar
input ENUM_TIMEFRAMES InpBotaoTF1 = PERIOD_M1; // Botao TF1
input ENUM_TIMEFRAMES InpBotaoTF2 = PERIOD_M5; // Botao TF2
input ENUM_TIMEFRAMES InpBotaoTF3 = PERIOD_M15; // Botao TF3
//--- Constantes internas (nao aparecem nos inputs do EA)
const bool InpBloquearSeInvalida = true;
const bool InpEnviarTelemetria = true;
const int InpIntervaloCheckMin = 60;
const string BASE_URL = "https://bortolaia.com.br";
//+------------------------------------------------------------------+
//| Gera o HMAC-SHA256 para seguranca da transmissao |
//+------------------------------------------------------------------+
void StringToUtf8NoNull(const string value, char &buffer[])
{
StringToCharArray(value, buffer, 0, WHOLE_ARRAY, CP_UTF8);
while(ArraySize(buffer) > 0 && buffer[ArraySize(buffer)-1] == 0)
ArrayResize(buffer, ArraySize(buffer)-1);
}
string Sha256Hex(const string value)
{
char source[];
uchar data[], key[], hash[];
StringToUtf8NoNull(value, source);
ArrayResize(data, ArraySize(source));
for(int i=0; i<ArraySize(source); i++) data[i] = (uchar)source[i];
ArrayResize(key, 0);
int res = CryptEncode(CRYPT_HASH_SHA256, data, key, hash);
if(res <= 0) return "";
string hex = "";
for(int i=0; i<ArraySize(hash); i++) hex += StringFormat("%02x", hash[i]);
return hex;
}
string ApiSignature(const string body)
{
return Sha256Hex(body + InpLicenseKey);
}
//==================== ESTADO GLOBAL ================================
bool g_licenca_ok = false;
string g_plano = "";
int g_dias_restantes = -1;
datetime g_ultimo_envio = 0;
//+------------------------------------------------------------------+
//| Leitor minimalista de JSON plano (chave -> valor) |
//+------------------------------------------------------------------+
string JsonGet(const string json, const string campo)
{
string alvo = "\"" + campo + "\":";
int p = StringFind(json, alvo);
if(p < 0) return "";
p += StringLen(alvo);
while(p < StringLen(json) &&
(StringGetCharacter(json,p) == ' ' || StringGetCharacter(json,p) == '"')) p++;
int fim = p;
while(fim < StringLen(json))
{
ushort c = StringGetCharacter(json, fim);
if(c == ',' || c == '}' || c == '"') break;
fim++;
}
return StringSubstr(json, p, fim - p);
}
//+------------------------------------------------------------------+
//| POST form-urlencoded generico com tratamento de WebRequest |
//+------------------------------------------------------------------+
int PostForm(const string endpoint, const string body, string &resposta)
{
char post[], result[];
string result_headers;
string signature = ApiSignature(body);
string headers = "Content-Type: application/x-www-form-urlencoded\r\n" +
"X-Signature: " + signature + "\r\n";
StringToUtf8NoNull(body, post);
ResetLastError();
int code = WebRequest("POST", BASE_URL + endpoint, headers, 8000,
post, result, result_headers);
if(code == -1)
{
Print("[BortolaIA-X] WebRequest bloqueado (erro ", GetLastError(), ").");
Print("[BortolaIA-X] Autorize ", BASE_URL,
" em Ferramentas > Opcoes > Expert Advisors > WebRequest.");
resposta = "";
return -1;
}
resposta = CharArrayToString(result, 0, WHOLE_ARRAY, CP_UTF8);
return code;
}
//+------------------------------------------------------------------+
//| Converte datetime para ISO-8601 UTC (exigido pela API) |
//+------------------------------------------------------------------+
string IsoTime(datetime t)
{
MqlDateTime d;
TimeToStruct(t, d);
return StringFormat("%04d-%02d-%02dT%02d:%02d:%02d.000Z",
d.year, d.mon, d.day, d.hour, d.min, d.sec);
}
//+------------------------------------------------------------------+
//| Valida a licenca desta conta MT5 contra o servidor |
//+------------------------------------------------------------------+
bool ValidarLicenca()
{
if(StringLen(InpLicenseKey) < 15)
{
Print("[BortolaIA-X] ERRO: Chave de licenca invalida ou ausente. Insira sua chave nos parametros (ex: EA-XXXX-XXXX-XXXX).");
return false;
}
long conta = AccountInfoInteger(ACCOUNT_LOGIN);
string broker = AccountInfoString(ACCOUNT_COMPANY);
string hardware = TerminalInfoString(TERMINAL_CPU_NAME) + TerminalInfoString(TERMINAL_PATH);
string body = "key=" + InpLicenseKey
+ "&account=" + (string)conta
+ "&broker=" + broker
+ "&hardware_id=" + hardware;
string resposta = "";
int code = PostForm("/api/public/license/validate", body, resposta);
if(code < 0) return false;
if(JsonGet(resposta, "valid") != "true")
{
Print("[BortolaIA-X] Licenca recusada: ", JsonGet(resposta, "message"),
" (codigo: ", JsonGet(resposta, "code"), ")");
return false;
}
g_plano = JsonGet(resposta, "plan");
g_dias_restantes = (int)StringToInteger(JsonGet(resposta, "days_left"));
Print("[BortolaIA-X] Licenca ATIVA | Conta ", conta,
" | Plano: ", g_plano, " | Dias restantes: ", g_dias_restantes);
return true;
}
//+------------------------------------------------------------------+
//| Envia uma operacao fechada para o painel |
//+------------------------------------------------------------------+
void EnviarTelemetria(ulong ticket, string simbolo, string tipo, double volume,
double preco_abertura, double preco_fechamento,
double lucro, double comissao, double swap,
datetime abertura, datetime fechamento)
{
if(!InpEnviarTelemetria) return;
long conta = AccountInfoInteger(ACCOUNT_LOGIN);
string body = "key=" + InpLicenseKey
+ "&account=" + (string)conta
+ "&ticket=" + (string)ticket
+ "&symbol=" + simbolo
+ "&type=" + tipo
+ "&volume=" + DoubleToString(volume, 2)
+ "&open_price=" + DoubleToString(preco_abertura, 5)
+ "&close_price=" + DoubleToString(preco_fechamento, 5)
+ "&profit=" + DoubleToString(lucro, 2)
+ "&commission=" + DoubleToString(comissao, 2)
+ "&swap=" + DoubleToString(swap, 2)
+ "&open_time=" + IsoTime(abertura)
+ "&close_time=" + IsoTime(fechamento);
string resposta = "";
int code = PostForm("/api/public/telemetry", body, resposta);
if(code != 200)
Print("[BortolaIA-X] Falha ao sincronizar operacao ", ticket,
" (HTTP ", code, ") ", resposta);
}
//+------------------------------------------------------------------+
//| Varre o historico recente e envia os negocios de saida |
//+------------------------------------------------------------------+
void SincronizarHistorico()
{
datetime de = (g_ultimo_envio == 0) ? TimeCurrent() - 86400 : g_ultimo_envio;
if(!HistorySelect(de, TimeCurrent())) return;
int total = HistoryDealsTotal();
for(int i = 0; i < total; i++)
{
ulong deal = HistoryDealGetTicket(i);
if(deal == 0) continue;
// Apenas negocios que ENCERRAM posicao possuem resultado financeiro
if(HistoryDealGetInteger(deal, DEAL_ENTRY) != DEAL_ENTRY_OUT) continue;
datetime fechamento = (datetime)HistoryDealGetInteger(deal, DEAL_TIME);
if(fechamento <= g_ultimo_envio) continue;
string simbolo = HistoryDealGetString(deal, DEAL_SYMBOL);
long tipo = HistoryDealGetInteger(deal, DEAL_TYPE);
double volume = HistoryDealGetDouble(deal, DEAL_VOLUME);
double preco = HistoryDealGetDouble(deal, DEAL_PRICE);
double lucro = HistoryDealGetDouble(deal, DEAL_PROFIT);
double com = HistoryDealGetDouble(deal, DEAL_COMMISSION);
double swap = HistoryDealGetDouble(deal, DEAL_SWAP);
// O tipo do negocio de saida e invertido em relacao a posicao original
string lado = (tipo == DEAL_TYPE_SELL) ? "buy" : "sell";
EnviarTelemetria(deal, simbolo, lado, volume, preco, preco,
lucro, com, swap, fechamento, fechamento);
}
g_ultimo_envio = TimeCurrent();
}
//+------------------------------------------------------------------+
int OnInit()
{
g_licenca_ok = ValidarLicenca();
if(!g_licenca_ok && InpBloquearSeInvalida)
{
Print("[BortolaIA-X] Licenca invalida para esta conta. O EA nao sera iniciado.");
return(INIT_FAILED);
}
EventSetTimer(MathMax(60, InpIntervaloCheckMin * 60));
return(INIT_SUCCEEDED);
}
//+------------------------------------------------------------------+
void OnTimer()
{
g_licenca_ok = ValidarLicenca();
if(!g_licenca_ok && InpBloquearSeInvalida)
{
Print("[BortolaIA-X] Licenca revogada ou expirada. Encerrando o EA.");
ExpertRemove();
return;
}
SincronizarHistorico();
}
//+------------------------------------------------------------------+
void OnTradeTransaction(const MqlTradeTransaction &trans,
const MqlTradeRequest &request,
const MqlTradeResult &result)
{
if(!g_licenca_ok) return;
if(trans.type == TRADE_TRANSACTION_DEAL_ADD) SincronizarHistorico();
}
//+------------------------------------------------------------------+
void OnTick()
{
if(!g_licenca_ok) return;
// >>> Insira aqui a logica de trading do seu Expert Advisor <<<
}
//+------------------------------------------------------------------+
void OnDeinit(const int reason)
{
EventKillTimer();
}
//+------------------------------------------------------------------+