Google Sheets é o scratchpad do analista de macros: rápido de atualizar, fácil de compartilhar e já conectado ao resto do espaço de trabalho do Google. Google Apps Script O tempo de execução JavaScript incorporado no Google Sheet permite que você chame qualquer API REST de uma planilha sem sair do navegador. UrlFetchApp, lidar com limites de taxa e tentativas de reinicialização, normalizar respostas de múltiplos indicadores em linhas limpas e agendar atualizações automáticas para que o painel de controle de macro permaneça atualizado sem qualquer intervenção manual.
O que você vai construir
- Um auxiliar de busca reutilizável liga para o FXMacroData através do
UrlFetchAppcom logia de tentativa de reinicialização e de retrocesso incorporada - Carregador de múltiplos indicadores recupera vários pares de moedas/indicadores e normaliza cada um numa linha plana de folha de cálculo
- Um escritor de folhas cria ou redefine uma aba com nome, escreve cabeçalhos e adiciona linhas a cada execução
- Um gatilho controlado pelo tempo actualiza automaticamente a folha todas as manhãs de dia útil
Requisitos prévios
- Conta do Google qualquer conta com acesso a Google Sheets e Apps Script
- Chave da API do FXMacroData Inscreva-se em / subscrever; muitos pontos finais de anúncio de USD são acessíveis ao público sem uma chave para testes iniciais
- Não há software adicional Apps Script é executado inteiramente no navegador; não é necessário Node.js, Python ou ferramentas locais
- Passo 1 -
Passo 1 Crie uma planilha do Google e abra o editor de scripts Apps
Abre . folhas.google.com e criar uma nova planilha em branco. Dê-lhe um nome descritivo como Painel de controle FXMacroDataDepois abre o editor de scripts:
- Clique . Extensões na barra de menus de cima.
- Selecione Apps Script- Não .
- O editor abre numa nova aba com padrão
Code.gsFicheiro. - Renomear o projeto (campo superior esquerdo) para FXMacroData Loader Para ser mais claro.
Todo o código que escreve aqui é executado no lado do servidor na infraestrutura do Google ele tem acesso ao completo UrlFetchApp O serviço pode ler e escrever a planilha através do
SpreadsheetApp Serviço.
Dica: guarde sua chave de API como uma propriedade de script
Nunca codifique sua chave de API diretamente no script.
Configurações do projeto → Propriedades do script → Adicionar propriedade e adicionar uma propriedade chamada
FXMACRODATA_API_KEY As funções auxiliares abaixo lêem esta propriedade no tempo de execução através do PropertiesService.getScriptProperties()- Não .
- Passo 2 -
Passo 2 Escreva um auxiliar de busca com lógica de reintent
Coloque o seguinte código em Code.gs, substituindo a função de espaço reservado. UrlFetchApp.fetch com retrocesso exponencial, de modo que erros transitórios ou breves respostas de limite de taxa não matam a execução inteira.
/**
* Fetches a FXMacroData endpoint with automatic retry and exponential back-off.
*
* @param {string} currency - e.g. "usd", "eur", "chf"
* @param {string} indicator - e.g. "policy_rate", "gdp", "inflation"
* @returns {Object|null} - Parsed JSON response, or null on permanent failure
*/
function fetchAnnouncement(currency, indicator) {
const apiKey = PropertiesService.getScriptProperties()
.getProperty('FXMACRODATA_API_KEY') || '';
const url = `https://fxmacrodata.com/api/v1/announcements/${currency}/${indicator}`
+ (apiKey ? `?api_key=${apiKey}` : '');
const maxRetries = 4;
let delay = 1000; // 1 second initial back-off
for (let attempt = 1; attempt <= maxRetries; attempt++) {
try {
const response = UrlFetchApp.fetch(url, { muteHttpExceptions: true });
const status = response.getResponseCode();
if (status === 200) {
return JSON.parse(response.getContentText());
}
if (status === 429) {
// Rate limited — honour the back-off and retry
Logger.log(`Rate limited on attempt ${attempt}. Waiting ${delay}ms.`);
Utilities.sleep(delay);
delay *= 2; // exponential back-off
continue;
}
if (status === 404) {
Logger.log(`No data for ${currency}/${indicator} (404). Skipping.`);
return null;
}
// Other non-retryable errors
Logger.log(`HTTP ${status} for ${currency}/${indicator}.`);
return null;
} catch (e) {
Logger.log(`Network error on attempt ${attempt}: ${e.message}`);
Utilities.sleep(delay);
delay *= 2;
}
}
Logger.log(`Permanently failed after ${maxRetries} attempts: ${currency}/${indicator}`);
return null;
}
Algumas coisas que vale a pena notar neste auxiliar:
muteHttpExceptions: trueimpede Apps Script de jogar em códigos não-200 você recebe o código de status e pode decidir o que fazer.- http 429 (Too Many Requests) desencadeia uma reatendimento com o dobro do atraso.
- http 404 - Não significa normalmente que o indicador ainda não está disponível para essa moeda o auxiliar retorna
nullEntão, a fila é ignorada. Logger.logA saída é visível em Visualização → Registros no editor de scripts Apps, tornando a depuração simples.
- Passo 3 -
Passo 3 Normalize as respostas JSON em linhas de planilha
O ponto final de anúncios FXMacroData retorna um único objeto por par de moedas/indicadores. Para criar uma planilha útil, você precisa nivelar uma lista de solicitações em uma estrutura de linha consistente. Adicione a seguinte função de normalização abaixo fetchAnnouncement- Não .
/**
* Converts a FXMacroData announcement response object into a flat array
* suitable for appending as a single Sheets row.
*
* Columns: Timestamp, Currency, Indicator, Value, Prior, Consensus,
* Announcement DateTime, Direction
*
* @param {Object} data - Parsed JSON from fetchAnnouncement()
* @returns {Array} - Flat row array
*/
function toRow(data) {
if (!data) return null;
const direction =
data.val > data.prior ? 'Beat' :
data.val < data.prior ? 'Miss' : 'In line';
return [
new Date().toISOString(), // Run timestamp
(data.currency || '').toUpperCase(),
(data.indicator || '').replace(/_/g, ' '),
data.val ?? '',
data.prior ?? '',
data.consensus ?? '',
data.announcement_datetime || '',
direction
];
}
O ... announcement_datetime O campo de FXMacroData tem uma precisão de segundo nível o momento exato em que o banco central ou a agência estatística publicou a leitura.
Sobre o ... consensus campo
Quando o campo está ausente, a API o omite do objeto de resposta, por isso data.consensus ?? '' escreve com segurança uma célula vazia em vez da cadeia "undefined"- Não .
- Passo 4 -
Passo 4 Escrever dados em uma guia do Google Sheets
Agora adicione a função de carregador principal que liga tudo: ele itera sobre uma lista de pares de moeda/indicador, chamadas fetchAnnouncement, converte cada resultado com
toRow, e anexa as linhas a uma aba de folha dedicada.
/**
* Defines the currency/indicator pairs to track.
* Extend this list to cover additional signals for your strategy.
*/
const INDICATORS = [
{ currency: 'usd', indicator: 'policy_rate' },
{ currency: 'usd', indicator: 'inflation' },
{ currency: 'usd', indicator: 'non_farm_payrolls' },
{ currency: 'eur', indicator: 'policy_rate' },
{ currency: 'eur', indicator: 'inflation' },
{ currency: 'chf', indicator: 'gdp' },
{ currency: 'chf', indicator: 'consumer_confidence' },
{ currency: 'chf', indicator: 'gov_bond_10y' },
{ currency: 'gbp', indicator: 'policy_rate' },
{ currency: 'gbp', indicator: 'inflation' },
];
const SHEET_NAME = 'MacroData';
const HEADERS = [
'Run Timestamp', 'Currency', 'Indicator', 'Value',
'Prior', 'Consensus', 'Announcement DateTime', 'Direction'
];
/**
* Main entry point — fetches all configured indicators and
* appends new rows to the MacroData sheet tab.
*/
function loadMacroData() {
const ss = SpreadsheetApp.getActiveSpreadsheet();
let sheet = ss.getSheetByName(SHEET_NAME);
// Create the tab if it does not exist yet
if (!sheet) {
sheet = ss.insertSheet(SHEET_NAME);
sheet.appendRow(HEADERS);
sheet.getRange(1, 1, 1, HEADERS.length)
.setFontWeight('bold')
.setBackground('#1a73e8')
.setFontColor('#ffffff');
sheet.setFrozenRows(1);
}
// Build a set of existing announcement_datetimes to avoid duplicates
const lastRow = sheet.getLastRow();
const existing = new Set();
if (lastRow > 1) {
const dtCol = 7; // "Announcement DateTime" is column 7 (index 6, 1-based col 7)
const values = sheet.getRange(2, dtCol, lastRow - 1, 1).getValues();
values.forEach(([dt]) => { if (dt) existing.add(String(dt)); });
}
const newRows = [];
INDICATORS.forEach(({ currency, indicator }) => {
// Throttle requests — 200 ms between calls keeps well within rate limits
Utilities.sleep(200);
const data = fetchAnnouncement(currency, indicator);
const row = toRow(data);
if (!row) return; // skip null / error responses
const announcementDt = row[6]; // announcement_datetime column
if (existing.has(announcementDt)) {
Logger.log(`Skipping duplicate: ${currency}/${indicator} @ ${announcementDt}`);
return;
}
newRows.push(row);
existing.add(announcementDt); // guard against duplicates within the same run
});
if (newRows.length > 0) {
sheet.getRange(sheet.getLastRow() + 1, 1, newRows.length, HEADERS.length)
.setValues(newRows);
Logger.log(`Appended ${newRows.length} new row(s) to "${SHEET_NAME}".`);
} else {
Logger.log('No new rows — all announcements already present.');
}
}
Corram . loadMacroData manualmente do editor (clique ▶ CorraA primeira execução irá solicitar-lhe para autorizar o script clique
Permissões de revisão → Permitir Para conceder acesso à planilha e às solicitações de rede externa.
Dica: adicionar uma função "Reset" para desenvolvimento
Durante o desenvolvimento é útil limpar a folha e re-executar a partir do zero.
function resetSheet() {
const ss = SpreadsheetApp.getActiveSpreadsheet();
const sheet = ss.getSheetByName(SHEET_NAME);
if (sheet) ss.deleteSheet(sheet);
loadMacroData(); // recreates with fresh headers and data
}
- Passo 5 -
Passo 5 Limitações de taxa de gestão em grandes escalas de indicadores
O atraso de 200 ms entre solicitações no Passo 4 é suficiente para a lista de dez indicadores mostrada acima. Se você expandir para 50 ou mais pares cobrindo várias moedas em todo o catálogo de anúncios completo , você deve implementar um estrangulamento mais deliberado. Substitua o sono constante por uma pausa baseada em contador:
/**
* Fetches a larger list of indicators with adaptive throttling.
* Pauses for 1 second every 10 requests to respect rate limits.
*/
function loadMacroDataBulk(indicators) {
const ss = SpreadsheetApp.getActiveSpreadsheet();
let sheet = ss.getSheetByName(SHEET_NAME) || (() => {
const s = ss.insertSheet(SHEET_NAME);
s.appendRow(HEADERS);
s.getRange(1, 1, 1, HEADERS.length)
.setFontWeight('bold')
.setBackground('#1a73e8')
.setFontColor('#ffffff');
s.setFrozenRows(1);
return s;
})();
const newRows = [];
let count = 0;
indicators.forEach(({ currency, indicator }) => {
count++;
// Longer pause every 10 requests
if (count % 10 === 0) {
Logger.log(`Pausing after ${count} requests…`);
Utilities.sleep(1500);
} else {
Utilities.sleep(150);
}
const data = fetchAnnouncement(currency, indicator);
const row = toRow(data);
if (row) newRows.push(row);
});
if (newRows.length > 0) {
sheet.getRange(sheet.getLastRow() + 1, 1, newRows.length, HEADERS.length)
.setValues(newRows);
}
Logger.log(`Bulk load complete — ${newRows.length} rows appended.`);
}
O ponto final de anúncio do FXMacroData é rápido cada resposta normalmente retorna em menos de 100 ms de um ambiente de execução de script do Google Apps. Utilities.sleep é a maneira mais simples de ficar dentro dos limites do seu plano sem batch ou cache lógica.
- O passo 6 .
Passo 6 Agendar atualizações automáticas com um gatilho orientado pelo tempo
Aplicações do Script. Ativadores O assistente seguinte cria um gatilho de manhã de dia de semana programaticamente execute-o uma vez do editor para registrá-lo, em seguida, excluir o próprio assistente:
/**
* Registers a time-driven trigger that runs loadMacroData()
* every weekday between 07:00 and 08:00 UTC.
*
* Run this function ONCE from the Apps Script editor to set up the trigger.
* You do not need to call it again — it persists in the project.
*/
function createWeekdayTrigger() {
// Remove any existing triggers for loadMacroData to avoid duplicates
ScriptApp.getProjectTriggers()
.filter(t => t.getHandlerFunction() === 'loadMacroData')
.forEach(t => ScriptApp.deleteTrigger(t));
ScriptApp.newTrigger('loadMacroData')
.timeBased()
.everyWeeks(1)
.onWeekDay(ScriptApp.WeekDay.MONDAY)
.atHour(7)
.create();
// Also register Tuesday through Friday
[
ScriptApp.WeekDay.TUESDAY,
ScriptApp.WeekDay.WEDNESDAY,
ScriptApp.WeekDay.THURSDAY,
ScriptApp.WeekDay.FRIDAY,
].forEach(day => {
ScriptApp.newTrigger('loadMacroData')
.timeBased()
.everyWeeks(1)
.onWeekDay(day)
.atHour(7)
.create();
});
Logger.log('Weekday triggers registered for loadMacroData.');
}
Depois de correr . createWeekdayTrigger- Abre . Ativadores (icono de alarme-sino na barra lateral esquerda do editor) para confirmar cinco gatilhos aparecem um para cada dia da semana. Cada gatilho dispara entre 07:00 e 08:00 no fuso horário configurado para a sua conta do Google.
Alinhamento com o calendário de lançamento
Para uma abordagem mais cirúrgica, consulte o Ponto final do calendário de lançamento do FXMacroData No início de cada semana, para encontrar dias com anúncios de alto impacto programados, em seguida, apenas execute a varredura completa do indicador nesses dias.
- O passo 7 .
Passo 7 Pega no calendário de lançamento para pré-filtrar por dias de evento
O ... /v1/calendar/{currency} Endpoint retorna as próximas versões programadas para uma moeda. Use-o na segunda-feira para criar um conjunto de datas de anúncio para a semana, em seguida, pule a etapa de busca em dias sem eventos isso evita chamadas desnecessárias de API em semanas tranquilas.
/**
* Returns a Set of date strings ("YYYY-MM-DD") for which at least one
* high-impact announcement is scheduled this week for the given currency.
*
* @param {string} currency - e.g. "usd"
* @returns {Set} */ função getAnnouncementDatesThisWeek(currency) { const apiKey = PropertiesService.getScriptProperties() .getProperty (('FXMACRODATA.getCode.getResponse.API_KEY') '; const url = https://fxmacrodata.com/api/v1/calendar/${currency}` + (apiKey ? `(((?api_key=${apiKee}` : ' ' '); const response = UrlFetch mute.Appfetch, {HttpExceptions: true }); if (response.getCalCode) == 200) = newset; const const retranslate = new); const Date fetches = JSON.Macdate.getFetchedData.getContent.getTexts (); const const const rest rest restores = new dates; const restores (data); constores (date); const restored (date) = data); const for today; const for $ (date).getData (date;data); restores; constores; restores de data); rest restored for today (date (date), *constores); const data const constores = data; const 'date); restored' (date'; restores 'date'; 'date;date); 'date'); const 'today); const;date (day); const (date}); const ('date); (date'): 'todays); 'todday); 'day); (day'); (date) (date=today;date; (date-day); ('today) (day) (d) (today). (date)). (date.events); (events) (event); (data) (de) (e) (s) (v.event) (D) (c) (this) (current) (a) (today) (ex.d) {events). (dete) (This) (new) (Event) (r) (n.d.d). (ev.d.) (d.e.d); (e.e) {d) } (d.) { (d} (d); 'e) }) (now) (no); (todays
Para utilizar este padrão, registe-se loadMacroDataCalendarAware como o manipulador do gatilho em vez de loadMacroData substituir a cadeia de nomes de funções em createWeekdayTrigger
Em conformidade.
- Resumo -
Resumo
Agora você tem um pipeline completo, pronto para produção que conecta FXMacroData para Google Sheets através de Apps Script:
- Um auxiliar de busca com tentativa de reinicialização e back-off exponencial que lida com erros de rede transitórios e respostas de limite de taxa com graça.
- Uma função de normalização que converte cada resposta de anúncio em uma linha de planilha consistente e segura de deduplicação.
- Um redator de folhas que cria cabeçalhos na primeira execução, anexa apenas novos lançamentos e pula os vistos anteriormente
announcement_datetimevalores. - A redução adaptativa para barragens a granel em dezenas de pares de moedas/indicadores.
- Um gatilho de dia de semana para atualizações diárias totalmente automatizadas.
- Uma pré-verificação de calendário opcional que evita chamadas de API redundantes em dias sem lançamentos agendados.
Próximos passos
-
Aumentar a lista de indicadores consulte o catálogo completo em
/api-data-docs
e adicionar pares relevantes para a sua estratégia (por exemplo
chf/gov_bond_10y- Não .eur/pmi- Não .gbp/employment) -
Adicionar formatação condicional realçar linhas onde Direção É ...
Beat- Em verde e ...Missem vermelho usandoSpreadsheetAppÉ ...ConditionalFormatRuleBuilderpara leitura de sinal de um olhar. -
Alertas de push para Slack ou e-mail após adicionar linhas, utilizar
MailApp.sendEmailou uma chamada de webhook lá dentro .loadMacroDatapara notificar a sua equipa quando uma impressão de alta incidência chegar. -
Acompanhe os valores históricos o
Anúncios ponto final
também aceita .
start_date- Não .end_dateParâmetros executar um recheio único em um intervalo de datas mais longo para semear uma guia histórica ao lado da alimentação ao vivo.