Google Sheets est le scratchpad de l'analyste de macro: rapide à mettre à jour, facile à partager et déjà connecté au reste de l 'espace de travail Google. Google Apps Script Le temps d'exécution JavaScript intégré de Google Sheet vous permet d'appeler n'importe quelle API REST à partir d'une feuille de calcul sans quitter le navigateur. Ce guide vous explique comment extraire les données d'annonce FXMacroData dans un onglet Feuilles avec UrlFetchApp, gérer les limites de vitesse et les essais, normaliser les réponses multi-indicateurs en lignes propres et planifier les mises à jour automatiques afin que votre tableau de bord macro reste à jour sans aucune intervention manuelle.
Ce que vous allez construire
- Un assistant de récupération réutilisable appelle FXMacroData par le biais
UrlFetchAppavec logique de réessayage et de rétro-off intégrée - Un chargeur à indicateur multiple récupère plusieurs paires de devises/indicateurs et les normalise chacune dans une ligne de feuille de calcul plate
- Un écrivain de feuilles crée ou réinitialise un onglet nommé, écrit des en-têtes et ajoute des lignes à chaque exécution
- Un déclencheur à commande temporelle rafraîchit automatiquement la feuille tous les matins de semaine
Pré-requis
- Compte Google tout compte ayant accès à Google Sheets et Apps Script
- Clé de l'API FXMacroData inscrivez-vous à / souscrivez; de nombreux points d' annonce en USD sont accessibles au public sans clé pour les tests initiaux
- Aucun logiciel supplémentaire Apps Script s'exécute entièrement dans le navigateur; aucun Node.js, Python ou outillage local n'est requis
- Passe 1 - Passez à la première étape
Étape 1 Créez une feuille Google et ouvrez l'éditeur de scripts Apps
Ouvrez . Les données sont fournies par le service de gestion des données. et créer une nouvelle feuille de calcul vide. Donnez-lui un nom descriptif tel que Le tableau de bord FXMacroDataAlors ouvrez l' éditeur de scripts:
- Cliquez Les extensions dans la barre de menu en haut.
- Sélectionnez Le script des applicationsJe suis désolé .
- L' éditeur s' ouvre dans un nouvel onglet par défaut
Code.gs- Je vous en prie. - Renommer le projet (en haut à gauche) à Le chargeur de FXMacroData Pour être plus clair.
Tout le code que vous écrivez ici fonctionne côté serveur sur l'infrastructure de Google il a accès à la totalité UrlFetchApp Le service de l'information peut lire et écrire la feuille de calcul via le
SpreadsheetApp - le service.
Astuce: stockez votre clé API comme propriété de script
Ne codez jamais votre clé API directement dans le script.
Paramètres du projet → Propriétés du script → Ajouter une propriété et ajouter une propriété nommée
FXMACRODATA_API_KEY Les fonctions d'assistance ci-dessous lisent cette propriété à l'exécution via PropertiesService.getScriptProperties()Je suis désolé .
- Passe 2 - Je suis désolé
Étape 2 Écrire un assistant de récupération avec logique de réessayage
Insérez le code suivant dans Code.gsCette aide enveloppe UrlFetchApp.fetch Les résultats de cette étude sont les suivants:
/**
* 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;
}
Quelques points à noter dans cet auxiliaire:
muteHttpExceptions: trueempêche Apps Script de lancer sur les codes non-200 vous obtenez le code d'état et pouvez décider quoi faire.- Je suis en train de lire . 429 autres (Too Many Requests) déclenche une nouvelle tentative avec un retard de doublement. FXMacroData applique des limites de taux par clé; une stratégie de back-off modeste maintient le script dans le budget à travers un balayage complet de l'indicateur.
- Je suis en train de lire . 404 - Je ne sais pas signifie généralement que l'indicateur n'est pas encore disponible pour cette devise l'assistant retourne
nullDonc la rangée est sautée. Logger.logla sortie est visible sous Affichage → Jours dans l'éditeur de scripts Apps, ce qui rend le débogage simple.
- Pas 3 - Je suis désolé
Étape 3 Normaliser les réponses JSON dans les lignes de feuille de calcul
Le point de terminaison des annonces FXMacroData renvoie un seul objet par paire de devises/indicateurs. Pour créer une feuille de calcul utile, vous devez aplatir une liste de demandes dans une structure de ligne cohérente. Ajoutez la fonction de normalisation suivante ci-dessous fetchAnnouncementJe suis désolé .
/**
* 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
];
}
Le announcement_datetime Le champ de FXMacroData a une précision de deuxième niveau le moment exact où la banque centrale ou l'agence statistique a publié la lecture. Cet horodatage est idéal comme clé de déduplication: vous pouvez vérifier si une ligne avec cet horodatagé existe déjà dans la feuille avant de l'ajouter, évitant ainsi les lignes en double lors de sorties répétées.
À propos de ... consensus champ
Lorsque le champ est absent, l'API l'omet de l'objet de réponse, donc data.consensus ?? '' écrit en toute sécurité une cellule vide au lieu de la chaîne "undefined"Je suis désolé .
- Passe 4 - Passez à la première étape
Étape 4 Écrire des données dans un onglet Google Sheets
Maintenant, ajoutez la fonction de chargement principale qui lie tout ensemble: il itère sur une liste de paires de devises/indicateurs, appels fetchAnnouncement, convertit chaque résultat en
toRow, et ajoute les lignes à un onglet de feuille dédié.
/**
* 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.');
}
}
- Je ne sais pas . loadMacroData manuellement depuis l'éditeur (cliquez ▶ CourezLa première exécution vous invitera à autoriser le script cliquez
Permis de révision → autoriser Pour accorder l'accès à la feuille de calcul et aux demandes de réseau externe.
Conseil: ajouter une fonction "Réinitialiser" pour le développement
Pendant le développement, il est utile de vider la feuille et de recommencer à partir de zéro.
function resetSheet() {
const ss = SpreadsheetApp.getActiveSpreadsheet();
const sheet = ss.getSheetByName(SHEET_NAME);
if (sheet) ss.deleteSheet(sheet);
loadMacroData(); // recreates with fresh headers and data
}
- Passe 5 - Je suis désolé
Étape 5 Modifier les limites de taux pour les plus grandes analyses d'indicateurs
Le délai de 200 ms entre les demandes à l'étape 4 est suffisant pour la liste des dix indicateurs indiquée ci-dessus. Si vous développez à 50 paires ou plus couvrant plusieurs devises dans le catalogue complet des annonces , vous devez mettre en œuvre un throttling plus délibéré. Remplacez le sommeil constant par une pause basée sur le compteur:
/**
* 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.`);
}
Le point final d'annonce FXMacroData est rapide chaque réponse renvoie généralement en moins de 100 ms d'un environnement d'exécution de Google Apps Script. Utilities.sleep est le moyen le plus simple de rester dans les limites de votre plan sans logique de batch ou de mise en cache.
- Pas 6 - Je suis désolé
Étape 6 Planifier les mises à jour automatiques avec un déclencheur basé sur le temps
Applications du script Les déclencheurs Le système vous permet d'exécuter n'importe quelle fonction sur un calendrier sans serveur dédié. L'assistant suivant crée un déclencheur du matin du jour de la semaine par programmation exécute-le une fois à partir de l'éditeur pour l'enregistrer, puis supprime l'assiste lui-même:
/**
* 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.');
}
Après la course . createWeekdayTrigger- Je suis désolé . Les déclencheurs (icône de sonnette d'alarme dans la barre latérale gauche de l'éditeur) pour confirmer cinq déclencheurs apparaissent un pour chaque jour de la semaine. Chaque déclenchement se déclenchera entre 07:00 et 08:00 dans le fuseau horaire configuré pour votre compte Google.
Alignement avec le calendrier de sortie
Pour une approche plus chirurgicale, consultez le Point de fin du calendrier de sortie de FXMacroData Au début de chaque semaine pour trouver les jours avec des annonces à fort impact, puis exécutez uniquement la balayage de l'indicateur complet ces jours-là.
- Pas 7 - Je suis désolé
Étape 7 Récupérer le calendrier de sortie pour pré-filtrer par jour d'événement
Le /v1/calendar/{currency} endpoint renvoie les prochaines versions prévues pour une devise. Utilisez-le le lundi pour créer un ensemble de dates d'annonce pour la semaine, puis sautez l'étape de récupération les jours sans événements cela évite les appels inutiles d'API pendant les semaines calmes.
/**
* 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} */ fonction 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 réponse = UrlFetch mute.Appfetch, {HttpExceptions: true }); if (response.getCalCode) == 200) = newset; const const retransporte Date = set; const retrends Date = new; const JSON.Macdate.getContrast.getData.getContent.getToday; const rest rest rest (date=new); const const const for Macdate; const for example, cont cont cont ([email protected]); const (date_date); const for today); const rest (data.getdate); rest (dates); const ${date_day); const; const (data_date_for_day) }; const {date_week); const {data_day}); const 'Runits'); const ('date_today'); 'date_end'); {date=date_evenance}); {data=date=event); {date_day'); (date-end); {day_day_end}); 'today); {event_day'; {date-day};} (date/event;}); (date-day); {cust-day;}} (dates); {end_day; } (date); {today) (date) (day); (day) (d) (de) (s); {to-day) {date); } (day; {date) {day); } {date} (day} (decust_day).This const const restes) (contract rest (d); {const) {data) {dates) {to_day (date; }); {if (date}}} {date = date); { (
Pour utiliser ce modèle, inscrivez-vous loadMacroDataCalendarAware comme le manipulateur de la gâchette au lieu de loadMacroData remplacer la chaîne de noms de fonction par createWeekdayTrigger
en conséquence.
- Résumé
Résumé
Vous avez maintenant un pipeline complet prêt à la production qui relie FXMacroData à Google Sheets via Apps Script:
- Un assistant de récupération avec réessayer et back-off exponentiel qui gère les erreurs de réseau transitoires et les réponses de limite de vitesse avec grâce.
- Une fonction de normalisation qui convertit chaque réponse d'annonce en une ligne de feuille de calcul cohérente et sûre de déduplication.
- Un rédacteur de feuille qui crée des en-têtes lors de la première exécution, ne joint que les nouvelles versions, et saute les précédentes vues
announcement_datetimeles valeurs. - Le contrôle de la conversion est effectué par un système de régulation de la volatilité.
- Un déclencheur basé sur le temps pour les mises à jour quotidiennes entièrement automatisées.
- Une pré-vérification optionnelle du calendrier qui évite les appels d'API redondants les jours où aucune version n'est prévue.
Les prochaines étapes
-
Élargir la liste des indicateurs consultez le catalogue complet à
/api-data-docs Il est temps de le faire.
et ajouter des paires pertinentes à votre stratégie (par exemple
chf/gov_bond_10yJe suis désolé .eur/pmiJe suis désolé .gbp/employment) -
Ajouter une mise en forme conditionnelle mettre en évidence les lignes où Direction C' est ça .
Beaten vert etMissen rouge en utilisantSpreadsheetAppJe suis ...ConditionalFormatRuleBuilderpour la lecture du signal au premier coup d'œil. -
Poussez les alertes vers Slack ou e-mail après avoir ajouté les lignes, utiliser
MailApp.sendEmailou un appel sur le net à l' intérieur .loadMacroDatapour informer votre équipe quand une empreinte d'impact élevé arrive. -
Suivre les valeurs historiques le
annonces point final
Il accepte aussi .
start_dateJe suis désolé .end_dateParamètres exécuter un remplissage unique sur une plage de dates plus longue pour semer un onglet historique à côté de l'alimentation en direct.