Google Sheets adalah scratchpad analis makro: cepat untuk memperbarui, mudah untuk berbagi, dan sudah terhubung ke ruang kerja Google lainnya. Google Apps Script Sheets' built-in JavaScript runtime memungkinkan Anda memanggil REST API dari spreadsheet tanpa meninggalkan browser. Panduan ini berjalan melalui menarik data pengumuman FXMacroData ke tab Sheets dengan UrlFetchApp, menangani batas laju dan uji ulang, normalisasi respons multi-indikator ke baris bersih, dan menjadwalkan pembaruan otomatis sehingga dasbor makro Anda tetap terkini tanpa intervensi manual.
Apa yang akan Anda bangun
- Pembantu mengambil yang dapat digunakan kembali panggilan FXMacroData melalui
UrlFetchAppdengan built-in try ulang dan back-off logika - Sebuah loader multi-indikator mengambil beberapa pasangan mata uang/indikator dan menormalkan masing-masing ke dalam baris spreadsheet datar
- Seorang penulis Sheets membuat atau mengatur ulang tab bernama, menulis header, dan menambahkan baris dengan setiap menjalankan
- Pemicu yang didorong waktu memperbarui lembar secara otomatis setiap pagi hari kerja
Persyaratan
- Akun Google setiap akun dengan akses ke Google Sheets dan Apps Script
- Kunci API FXMacroData daftar di /langganan; banyak titik akhir pengumuman USD dapat diakses publik tanpa kunci untuk pengujian awal
- Tidak ada perangkat lunak tambahan Aplikasi Script berjalan sepenuhnya di browser; tidak diperlukan Node.js, Python, atau alat lokal
- Langkah 1 -
Langkah 1 Buat Google Sheet dan buka editor skrip Apps
Buka. sheets.google.com dan buat spreadsheet kosong baru. berikan nama deskriptif seperti FXMacroData DashboardKemudian buka editor skrip:
- Klik Perpanjangan di menu bar atas.
- Pilih Aplikasi ScriptAku tidak tahu.
- Editor dibuka di tab baru dengan default
Code.gsfile. - Mengganti nama proyek (bidang kiri atas) menjadi FXMacroData Loader untuk kejelasan.
Semua kode yang Anda tulis di sini berjalan di sisi server pada infrastruktur Google memiliki akses ke penuh UrlFetchApp layanan dan dapat membaca dan menulis spreadsheet melalui
SpreadsheetApp layanan.
Tips: simpan kunci API Anda sebagai properti Script
Jangan pernah hard-code kunci API Anda langsung di script.
Pengaturan Proyek → Properti Skrip → Tambahkan properti dan tambahkan properti bernama
FXMACRODATA_API_KEY Dengan kunci Anda sebagai nilai. Fungsi pembantu di bawah ini membaca properti ini pada saat runtime melalui PropertiesService.getScriptProperties().
- Langkah 2 -
Langkah 2 Tulis bantuan pengambilan dengan logika uji ulang
Tempelkan kode berikut di Code.gs, menggantikan fungsi placeholder. UrlFetchApp.fetch dengan back-off eksponensial sehingga kesalahan sementara atau respons batas laju singkat tidak membunuh seluruh run.
/**
* 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;
}
Beberapa hal yang perlu diperhatikan dalam bantuan ini:
muteHttpExceptions: truemencegah Aplikasi Script dari melemparkan pada non-200 kode Anda mendapatkan kode status dan dapat memutuskan apa yang harus dilakukan.- HTTP 429 (Terlalu Banyak Permintaan) memicu upaya ulang dengan penundaan dua kali lipat. FXMacroData memberlakukan batas tingkat per kunci; strategi back-off sederhana menjaga skrip dalam anggaran di seluruh penggeser indikator penuh.
- HTTP 404 biasanya berarti indikator belum tersedia untuk mata uang tersebut pembantu kembali
nulljadi barisnya dilewatkan dengan bersih. Logger.logoutput terlihat di bawah Tampilan → Log di editor skrip aplikasi, membuat debugging mudah.
- Langkah 3 -
Langkah 3 Normalisasi tanggapan JSON ke baris spreadsheet
FXMacroData announcements endpoint mengembalikan satu objek per pasangan mata uang/indikator. Untuk membangun spreadsheet yang berguna, Anda perlu meratakan daftar permintaan ke dalam struktur baris yang konsisten. Tambahkan fungsi normalisasi berikut di bawah ini fetchAnnouncementAku tidak tahu.
/**
* 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
];
}
- Apa? announcement_datetime kolom dari FXMacroData membawa tingkat kedua presisi saat tepat bank sentral atau lembaga statistik menerbitkan pembacaan. time stamp itu ideal sebagai kunci deduplikasi: Anda dapat memeriksa apakah baris dengan timestamp ini sudah ada di lembar sebelum menambahkan, mencegah baris duplikat pada kali berulang.
Tentang... consensus lapangan
Tidak semua indikator membawa nilai konsensus / perkiraan. Ketika bidang tidak ada API melewatkannya dari objek respons, jadi data.consensus ?? '' dengan aman menulis sel kosong daripada string "undefined".
- Langkah 4 -
Langkah 4 Tulis data ke tab Google Sheets
Sekarang tambahkan fungsi loader utama yang menghubungkan semuanya bersama-sama: itu berulang-ulang di atas daftar pasangan mata uang / indikator, panggilan fetchAnnouncement, mengkonversi setiap hasil dengan
toRow, dan menambahkan baris ke tab lembar khusus.
/**
* 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.');
}
}
Larilah. loadMacroData dengan manual dari editor (klik ▶ LarilahUntuk mengaktifkan program ini, Anda harus mengklik tombol "Authorise" (mengaktifkan) untuk menguji pipeline sebelum mengatur pemicu.
Permit Review → Izinkan Untuk memberikan akses ke spreadsheet dan permintaan jaringan eksternal.
Tip: tambahkan fungsi "Reset" untuk pengembangan
Selama pengembangan, berguna untuk menghapus lembar dan menjalankan kembali dari awal.
function resetSheet() {
const ss = SpreadsheetApp.getActiveSpreadsheet();
const sheet = ss.getSheetByName(SHEET_NAME);
if (sheet) ss.deleteSheet(sheet);
loadMacroData(); // recreates with fresh headers and data
}
- Langkah 5 -
Langkah 5 Mengatur batas tingkat di seluruh penggeser indikator yang lebih besar
Penundaan 200 ms antar permintaan pada Langkah 4 cukup untuk daftar sepuluh indikator yang ditunjukkan di atas. Jika Anda memperluas ke 50 atau lebih pasangan yang mencakup beberapa mata uang di seluruh katalog pengumuman penuh Anda harus menerapkan penekanan yang lebih disengaja. Ganti tidur konstan dengan jeda berbasis konter:
/**
* 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.`);
}
FXMacroData pengumuman titik akhir cepat setiap respon biasanya kembali dalam waktu kurang dari 100 ms dari lingkungan eksekusi Google Apps Script. Utilities.sleep adalah cara termudah untuk tetap dalam batas rencana Anda tanpa batching atau caching logika.
- Langkah 6 -
Langkah 6 Jadwalkan pembaruan otomatis dengan pemicu yang didorong waktu
Aplikasi Script's Pemicu sistem memungkinkan Anda menjalankan fungsi apa pun pada jadwal tanpa dedicated server. Helper berikut membuat pemicu pagi hari kerja secara programmatic menjalankan sekali dari editor untuk mendaftarkannya, kemudian hapus helper itu sendiri:
/**
* 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.');
}
Setelah lari. createWeekdayTriggerBuka. Pemicu (ikon lonceng alarm di bilah sisi kiri editor) untuk mengkonfirmasi lima pemicu muncul satu untuk setiap hari kerja. Setiap pemicu menyala antara 07:00 dan 08:00 di zona waktu yang dikonfigurasi untuk akun Google Anda.
Menyelaraskan dengan kalender rilis
Untuk pendekatan yang lebih bedah, pertanyaan FXMacroData release endpoint kalender Pada awal setiap minggu untuk menemukan hari dengan jadwal pengumuman berdampak tinggi, kemudian hanya menjalankan penggeser indikator penuh pada hari-hari itu.
- Langkah 7 -
Langkah 7 Ambil kalender rilis untuk pra-filter dengan hari acara
- Apa? /v1/calendar/{currency} Endpoint mengembalikan rilis yang dijadwalkan untuk mata uang. Gunakan pada hari Senin untuk membangun satu set tanggal pengumuman untuk minggu, kemudian lewatkan langkah pengambilan pada hari tanpa acara ini menghindari panggilan API yang tidak perlu pada minggu yang tenang.
/**
* 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}
*/
function getAnnouncementDatesThisWeek(currency) {
const apiKey = PropertiesService.getScriptProperties()
.getProperty('FXMACRODATA_API_KEY') || '';
const url = `https://fxmacrodata.com/api/v1/calendar/${currency}`
+ (apiKey ? `?api_key=${apiKey}` : '');
const response = UrlFetchApp.fetch(url, { muteHttpExceptions: true });
if (response.getResponseCode() !== 200) return new Set();
const releases = JSON.parse(response.getContentText());
const today = new Date();
const weekEnd = new Date(today);
weekEnd.setDate(today.getDate() + 7);
const dates = new Set();
(releases || []).forEach(event => {
if (!event.release_date) return;
const d = new Date(event.release_date);
if (d >= today && d <= weekEnd) {
dates.add(event.release_date.slice(0, 10));
}
});
return dates;
}
/**
* Calendar-aware version of loadMacroData. * Only runs the full indicator fetch if today has scheduled releases. */ fungsi loadMacroDataCalendarAware() { const today = new Date().toISOString().slice(0, 10); const currencies = ['usd', 'eur', 'chf', 'gbp']; const hasEvents = currencies.some(c => { const dates = getAnnouncementDatesThisWeek(c); return dates.has(today); }); if (!hasEvents) { Logger.log`No scheduled releases for today (${today}).Skip fetch.`); return; } Logger .log`Release events found for ${todays}. Running full macro fetch .`); fetroMacrodata; }
Untuk menggunakan pola ini, daftarkan loadMacroDataCalendarAware sebagai pemegang pemicu alih-alih loadMacroData mengganti string nama fungsi dengan createWeekdayTrigger
Dengan demikian.
- Ringkasan -
Ringkasan
Anda sekarang memiliki pipa produksi siap lengkap yang menghubungkan FXMacroData ke Google Sheets melalui Aplikasi Script:
- Pembantu pengambilan dengan upaya ulang dan back-off eksponensial yang menangani kesalahan jaringan sementara dan respon batas kecepatan dengan anggun.
- Fungsi normalisasi yang mengkonversi setiap respon pengumuman menjadi baris spreadsheet yang konsisten dan aman untuk deduplikasi.
- Sebuah penulis lembar yang membuat header pada saat pertama kali dijalankan, hanya menambahkan rilis baru, dan melewatkan yang sebelumnya terlihat
announcement_datetimenilai. - Adaptive throttling untuk penggeser massal di puluhan pasangan mata uang / indikator.
- Pemicu yang dikendalikan waktu untuk pembaruan harian otomatis.
- Pemeriksaan awal kalender opsional yang menghindari panggilan API yang berlebihan pada hari-hari tanpa rilis terjadwal.
Langkah selanjutnya
-
Perluas daftar indikator melihat katalog lengkap di
/api-data-docs
dan tambahkan pasangan yang relevan dengan strategi Anda (misalnya
chf/gov_bond_10yAku akan pergi.eur/pmiAku akan pergi.gbp/employment) -
Tambahkan pemformatan bersyarat menyorot baris dimana Arah adalah
Beatdi hijau danMissdengan menggunakan warna merahSpreadsheetApp- Itu ...ConditionalFormatRuleBuilderuntuk membaca sinyal sekilas. -
Push alert ke Slack atau email setelah menambahkan baris, gunakan
MailApp.sendEmailatau panggilan webhook di dalam.loadMacroDatauntuk memberi tahu tim Anda ketika cetakan berdampak tinggi tiba. -
Pelacakan nilai-nilai historis yang
pengumuman titik akhir
juga menerima
start_date/end_dateparameter menjalankan satu kali backfill pada rentang tanggal yang lebih panjang untuk benih tab historis di samping feed hidup.