Códigos e Scripts Úteis

Vou abrir esse tópico, já que com a IA, muita coisa que era extremamente técnica hoje encontra soluções simples. Fica o espaço disponível para compartilhar códigos e scripts úteis, de preferência em ambiente amigável para nível básico e intermediário.

Deixo a primeiro página aqui dedicada para linkar diretamente os mais votados:

7 curtidas

PTAX Histórico

Deixo em primeiro um script simples para se obter a PTAX atualizada diretamente pelo Google Sheets utilizando a extensão (nativa) Apps Script:

Não é 100% automatizado, então você precisa:

  1. Criar uma aba chamada exatamente “PTAX Histórico”
  2. Ela deve conter 3 colunas pelo menos
  3. A primeira coluna deve ser da data
  4. A segunda deve ser do valor de compra
  5. A terceira do valor de venda
  6. O nome realmente não importa, mas criam-se os intervalos nomeados “Data”, “Compra”, “Venda”
  7. Vá no Apps Script e substitua o Code.gs pelo seguinte código e execute.
/**
 * Atualiza automaticamente as colunas de PTAX Compra e Venda
 * na aba "PTAX Histórico", com base em datas úteis listadas na coluna "Data".
 * Usa a API do Banco Central (SGS), com chamadas seguras e tolerância a falhas.
 */

function atualizarHistoricoPTAX() {
  const planilha = SpreadsheetApp.getActiveSpreadsheet();
  const aba = planilha.getSheetByName("PTAX Histórico");

  const intervaloDatas = planilha.getRangeByName("Data").getValues();
  const intervaloCompra = planilha.getRangeByName("Compra");
  const intervaloVenda = planilha.getRangeByName("Venda");

  const valoresCompra = intervaloCompra.getValues();
  const valoresVenda = intervaloVenda.getValues();

  for (let i = 0; i < intervaloDatas.length; i++) {
    const data = intervaloDatas[i][0];
    if (!data || Object.prototype.toString.call(data) !== "[object Date]") continue;

    const jaTemCompra = valoresCompra[i][0];
    const jaTemVenda = valoresVenda[i][0];

    // Pula se já houver valores válidos (não vazio, não N/D, não Erro)
    if (ehValorValido(jaTemCompra) && ehValorValido(jaTemVenda)) continue;

    const dataFormatada = Utilities.formatDate(new Date(data), "GMT-3", "dd/MM/yyyy");

    try {
      const urlCompra = `https://api.bcb.gov.br/dados/serie/bcdata.sgs.1/dados?formato=json&dataInicial=${dataFormatada}&dataFinal=${dataFormatada}`;
      const urlVenda  = `https://api.bcb.gov.br/dados/serie/bcdata.sgs.10813/dados?formato=json&dataInicial=${dataFormatada}&dataFinal=${dataFormatada}`;

      const respostaCompra = UrlFetchApp.fetch(urlCompra, {muteHttpExceptions: true});
      const respostaVenda  = UrlFetchApp.fetch(urlVenda, {muteHttpExceptions: true});

      const conteudoCompra = respostaCompra.getContentText().trim();
      const conteudoVenda  = respostaVenda.getContentText().trim();

      // Verifica se a resposta da API é JSON esperado (começa com [)
      if (!conteudoCompra.startsWith("[") || !conteudoVenda.startsWith("[")) {
        Logger.log(`⚠️ Conteúdo inesperado para ${dataFormatada}. Pulando linha ${i + 2}.`);
        intervaloCompra.getCell(i + 1, 1).setValue("N/D");
        intervaloVenda.getCell(i + 1, 1).setValue("N/D");
        continue;
      }

      const dadosCompra = JSON.parse(conteudoCompra);
      const dadosVenda  = JSON.parse(conteudoVenda);

      if (dadosCompra.length > 0 && dadosVenda.length > 0) {
        const valorCompra = parseFloat(dadosCompra[0].valor.replace(",", "."));
        const valorVenda  = parseFloat(dadosVenda[0].valor.replace(",", "."));

        intervaloCompra.getCell(i + 1, 1).setValue(valorCompra);
        intervaloVenda.getCell(i + 1, 1).setValue(valorVenda);
        Logger.log(`✅ ${dataFormatada} atualizada: Compra ${valorCompra} / Venda ${valorVenda}`);
      } else {
        intervaloCompra.getCell(i + 1, 1).setValue("N/D");
        intervaloVenda.getCell(i + 1, 1).setValue("N/D");
        Logger.log(`⚠️ Sem dados retornados para ${dataFormatada}. Marcado como N/D.`);
      }

      Utilities.sleep(300); // Respeita limite da API
    } catch (e) {
      intervaloCompra.getCell(i + 1, 1).setValue("Erro");
      intervaloVenda.getCell(i + 1, 1).setValue("Erro");
      Logger.log(`❌ Erro ao buscar PTAX para ${dataFormatada} (linha ${i + 2}): ${e}`);
    }
  }

  Logger.log("✅ PTAX atualizada até " + new Date().toLocaleString("pt-BR"));
}

/**
 * Verifica se o valor é preenchido e válido.
 */
function ehValorValido(valor) {
  return valor !== "" && valor !== "N/D" && valor !== "Erro";
}

Uma dica, você pode criar uma outra aba com os feriados do ano para filtrar os dias, então, na célula A2, da data, você pode colocar 02/01/2024, para marcar o início, e depois criar uma fórmula no seguinte estilo:

=SE(A2<>“”; DIATRABALHO.INTL(A2; 1; 1; Feriados!A$2:A); “”)

Eu não sugiro automatizar totalmente a obtenção da PTAX pois o BC tem feriados técnicos que podem surgir como dias úteis ou serem pontos facultativos. De toda forma, é uma forma de evitar uma poluição de dados muito grande e você obtém os dados direto do Banco Central. Eu fortemente sugiro checagem manual se houve cotação no dia de interesse.

6 curtidas

PTAX Histórico Janela 7 dias

Aqui apenas uma continuação (ou substituição, cumprida a etapa acima PTAX Histórico), pois você não quer um script correndo 5000 dias todas as vezes que é executado (não esqueça de criar um gatilho diário):. Então, depois de executar o acima, siga com esse aqui:

function atualizarPTAXJanela7dias() {
  const planilha = SpreadsheetApp.getActiveSpreadsheet();
  const aba = planilha.getSheetByName("PTAX Histórico");

  const intervaloDatas = planilha.getRangeByName("Data").getValues();
  const intervaloCompra = planilha.getRangeByName("Compra").getValues();
  const intervaloVenda = planilha.getRangeByName("Venda").getValues();

  const hoje = new Date();
  const hojeLimpo = new Date(hoje.getFullYear(), hoje.getMonth(), hoje.getDate());

  for (let i = 0; i < intervaloDatas.length; i++) {
    const data = intervaloDatas[i][0];
    if (!data || Object.prototype.toString.call(data) !== "[object Date]") continue;

    const compra = intervaloCompra[i][0];
    const venda = intervaloVenda[i][0];

    const dataLimpa = new Date(data.getFullYear(), data.getMonth(), data.getDate());
    const diasDiferenca = (dataLimpa - hojeLimpo) / (1000 * 60 * 60 * 24);

    if (diasDiferenca < -7 || diasDiferenca > 7) continue; // fora da janela de 7 dias
    if (ehValorValido(compra) && ehValorValido(venda)) continue;

    const dataFormatada = Utilities.formatDate(dataLimpa, "GMT-3", "dd/MM/yyyy");

    try {
      const urlCompra = `https://api.bcb.gov.br/dados/serie/bcdata.sgs.1/dados?formato=json&dataInicial=${dataFormatada}&dataFinal=${dataFormatada}`;
      const urlVenda  = `https://api.bcb.gov.br/dados/serie/bcdata.sgs.10813/dados?formato=json&dataInicial=${dataFormatada}&dataFinal=${dataFormatada}`;

      const respostaCompra = UrlFetchApp.fetch(urlCompra, {muteHttpExceptions: true});
      const respostaVenda  = UrlFetchApp.fetch(urlVenda, {muteHttpExceptions: true});

      const conteudoCompra = respostaCompra.getContentText().trim();
      const conteudoVenda  = respostaVenda.getContentText().trim();

      if (!conteudoCompra.startsWith("[") || !conteudoVenda.startsWith("[")) {
        Logger.log(`⚠️ Sem retorno para ${dataFormatada}. Pulando linha ${i + 2}.`);
        continue;
      }

      const dadosCompra = JSON.parse(conteudoCompra);
      const dadosVenda  = JSON.parse(conteudoVenda);

      if (dadosCompra.length > 0 && dadosVenda.length > 0) {
        const valorCompra = parseFloat(dadosCompra[0].valor.replace(",", "."));
        const valorVenda  = parseFloat(dadosVenda[0].valor.replace(",", "."));

        aba.getRange(i + 2, 2).setValue(valorCompra);
        aba.getRange(i + 2, 3).setValue(valorVenda);
        Logger.log(`✅ ${dataFormatada} atualizada: Compra ${valorCompra} / Venda ${valorVenda}`);
      } else {
        Logger.log(`⚠️ Nenhum dado para ${dataFormatada}.`);
      }

      Utilities.sleep(300);
    } catch (e) {
      aba.getRange(i + 2, 2).setValue("Erro");
      aba.getRange(i + 2, 3).setValue("Erro");
      Logger.log(`❌ Erro ao buscar PTAX para ${dataFormatada} (linha ${i + 2}): ${e}`);
    }
  }

  Logger.log("✅ Janela dinâmica de 7 dias concluída às " + new Date().toLocaleString("pt-BR"));
}

function ehValorValido(valor) {
  return valor !== "" && valor !== "N/D" && valor !== "Erro";
}

Resultado:
Captura de Tela 2025-06-20 às 18.24.37

8 curtidas