Google Sheets هو لوحة الحفر للمحلل الكلي: سريعة التحديث وسهلة المشاركة ، ومرتبطة بالفعل ببقية مساحة عمل Google. يسمح لك Google Apps Script Runtime JavaScript المدمج في Sheets بـ استدعاء أي REST API من جدول بيانات دون مغادرة المتصفح. هذا الدليل يمر من خلال سحب بيانات إعلان FXMacroData إلى علامة تبويب Sheets مع UrlFetchApp، التعامل مع حدود المعدل وإعادة المحاولات، وتطبيع استجابات المؤشرات المتعددة في الصفوف النظيفة، وتحديد المواعيد التجديد التلقائي حتى لوحة التحكم الكبيرة الخاصة بك تبقى الحالية دون أي تدخل يدوي.
ما الذي ستبنيه
- مساعدة استرجاع قابلة لإعادة الاستخدام يتصل بـ FXMacroData عبر
UrlFetchAppمع محاولة إعادة مدمجة و منطق إعادة التوقف - محمول متعدد المؤشرات يحصل على عدة أزواج عملات/مؤشرات ويعادلها كل أزوج إلى صف من جدول تبريدي مسطح
- كاتب أوراق يخلق أو يعيد تعيين علامة تبويب تحمل اسم، ويكتب رؤوسًا، ويرسل صفوفًا مع كل تشغيل
- -مُحركٌ مدفوعٌ بالزمن يجدد الورقة تلقائيا كل صباح يوم أسبوعي
الشروط المسبقة
- حساب جوجل أي حساب مع الوصول إلى Google Sheets و Apps Script
- مفتاح FXMacroData API التسجيل في / اشترك؛ العديد من نقاط نهاية الإعلانات بالدولار متاحة للجمهور دون مفتاح للاختبار الأولي
- لا يوجد برنامج إضافي تطبيقات البرامج النصية تعمل بالكامل في المتصفح؛ لا تحتاج إلى Node.js أو Python أو أدوات محلية
الخطوة الأولى
الخطوة 1 إنشاء ورقة جوجل وفتح محرر البرامج
افتح sheets.google.com وخلق جدول بيانات جديد فارغ. أعطيه اسم وصفي مثل لوحة التحكم في FXMacroDataثم افتح محرر النصوص
- انقر التوسعات في شريط القائمة العلوي
- اختر برنامج تطبيقات. .
- المحرر يفتح في علامة تبويب جديدة باستخدام الافتراضية
Code.gsملف - قم بتغيير اسم المشروع (المنطقة العلوية اليسرى) إلى محمول FXMacroData من أجل الوضوح
كل الرموز التي تكتبها هنا تعمل على جانب الخادم على بنية تحتية جوجل UrlFetchApp ويمكنك قراءة و كتابة جدول البيانات من خلال
SpreadsheetApp الخدمة
نصيحة: تخزين مفتاح API الخاص بك كخصائص النص
لا تقوم بتشفير مفتاح واجهة برمجة المعلومات مباشرة في البرنامج
إعدادات المشروع → خصائص النص → إضافة الممتلكات و أضف خصائص اسمها
FXMACRODATA_API_KEY مع مفتاحك كقيمة. وظائف المساعدة أدناه تقرأ هذه الملكية في وقت التشغيل عن طريق PropertiesService.getScriptProperties().
الخطوة الثانية
الخطوة 2 اكتب مساعد استرجاع مع منطق إعادة التجربة
اضغط على الرمز التالي Code.gs، استبدال وظيفة حائز المواقع. هذا المساعد يلف UrlFetchApp.fetch مع تعاقب أساسي لذلك الأخطاء العابرة أو ردود الفعل المختصرة الحد من السرعة لا تقتل الجري بأكملها.
/**
* 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;
}
بعض الأشياء التي تستحق الإشارة في هذا المساعد:
muteHttpExceptions: trueيمنع برنامج Apps Script من إلقاء على رموز غير 200 تحصل على رمز الحالة ويمكنك أن تقرر ما يجب القيام به.- هتت ب 429 (طلبات كثيرة جداً) يؤدي إلى إعادة المحاولة مع تأخير مضاعف. تفرض FXMacroData حدود معدل لكل مفتاح؛ استراتيجية متواضعة للعودة إلى الوراء تبقي النص ضمن الميزانية عبر مسح مؤشر كامل.
- هتت ب 404 عادة ما يعني أن المؤشر غير متاح بعد لتلك العملة يعود المساعد
nullلذا تم تخطي الصف بشكل نظيف Logger.logالخروج مرئي تحت عرض → سجلات في محرر البرامج، مما يجعل التحديثات بسيطة.
الخطوة الثالثة
الخطوة 3 تقليد استجابات JSON في صفوف جدول البيانات
تعيد نقطة نهاية إعلانات FXMacroData كائنًا واحدًا لكل زوج عملة / مؤشر. لبناء جدول بيانات مفيد ، تحتاج إلى تسطيح قائمة الطلبات إلى بنية سطر متسقة. أضف وظيفة التطبيع التالية أدناه fetchAnnouncement.
/**
* 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
];
}
- ... announcement_datetime يحمل هذا المجال من FXMacroData دقة مستوى ثان اللحظة الدقيقة التي نشر فيها البنك المركزي أو وكالة الإحصاء القراءة. هذا الطابع الزمني مثالي كمفتاح لتحديد التكرار: يمكنك التحقق مما إذا كان صف مع هذا الطوابع الزمنية موجودًا بالفعل في الورقة قبل إضافته ، مما يمنع الصفوف المكررة على عمليات متكررة.
بخصوص consensus الحقل
لا تحمل جميع المؤشرات قيمة توافق / توقعات. عندما يكون الحقل غائباً ، فإن واجهة برمجة التطبيقات (API) تعتزل عن كائن الاستجابة ، لذلك data.consensus ?? '' يكتب خلية فارغة بدلا من السلسلة "undefined".
الخطوة الرابعة
الخطوة 4 اكتب البيانات في علامة تبويب Google Sheets
الآن أضف وظيفة المُحمل الرئيسي التي تربط كل شيء معًا: فإنه يتكرر عبر قائمة من أزواج العملات / المؤشرات، fetchAnnouncement، يحول كل نتيجة إلى
toRow، ويربط الصفوف إلى علامة تبويب ورقة مخصصة.
/**
* 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.');
}
}
إهرب loadMacroData يدويا من المحرر (انقر ▶ اهرب) لاختبار خط الأنابيب قبل تعيين محرك. أول تشغيل سوف يطلب منك أن تسمح البرنامج النصي انقر
إعادة النظر في الأذونات → السماح لمنح الوصول إلى جدول البيانات وطلبات الشبكة الخارجية.
نصيحة: إضافة وظيفة "إعادة تعيين" للتطوير
أثناء التطوير من المفيد مسح الورقة وإعادة تشغيلها من الصفر. أضف مساعد صغير:
function resetSheet() {
const ss = SpreadsheetApp.getActiveSpreadsheet();
const sheet = ss.getSheetByName(SHEET_NAME);
if (sheet) ss.deleteSheet(sheet);
loadMacroData(); // recreates with fresh headers and data
}
الخطوة الخامسة
الخطوة 5 التعامل مع حدود معدل التداول عبر مسحات مؤشرات أكبر
تأخر 200 ميس بين الطلبات في الخطوة 4 كافٍ لقائمة المؤشرات العشر الموضحة أعلاه. إذا قمت بتوسيع إلى 50 زوجًا أو أكثر تغطي عملات متعددة عبر كتالوج الإعلان الكامل يجب عليك تنفيذ خفض أكثر عمداً. استبدل النوم المستمر بتوقف قائم على العداد:
/**
* 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 سريعة كل استجابة عادة ما تعود في أقل من 100 ميس من بيئة تنفيذ Google Apps Script. القضيب الرئيسي في عمليات مسح كبيرة هو ميزانية طلب لكل مفتاح بدلاً من فترة تأخير ؛ المسافة بين المكالمات مع Utilities.sleep هي أبسط طريقة للبقاء ضمن حدود خطتك دون حزم أو تخزين الخزانة.
الخطوة السادسة
الخطوة 6 جدولة التحديثات التلقائية مع محرك مدفوع بالوقت
تطبيقات السكريبت أسباب يسمح لك نظام تشغيل أي وظيفة على جدول بدون خادم مخصص. يخلق المساعد التالي محركًا صباحيًا يومًا أسبوعيًًا برمجياً تشغيله مرة واحدة من المحرر لتسجيله ، ثم حذف المساعد نفسه:
/**
* 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.');
}
بعد الهروب createWeekdayTrigger، مفتوحة أسباب (رمز جرس الإنذار في الشريط الجانبي الأيسر من المحرر) للتأكيد تظهر خمس مفاتيح واحدة لكل يوم أسبوعي. كل مفاتيل يطلق بين الساعة 7:00 و 08:00 في المنطقة الزمنية التي تم تكوينها لحسابك في Google.
التوافق مع تقويم الإصدار
للحصول على نهج أكثر جراحية، استفسار نقطة نهاية تقويم إصدار FXMacroData في بداية كل أسبوع للعثور على أيام مع إعلانات ذات تأثير كبير المقررة، ثم تشغيل مسح المؤشر الكامل فقط في تلك الأيام. وهذا يبقي وقت التنفيذ قصير واستخدام واجهة برمجة التطبيقات منخفضة في أسابيع التقويم الهادئة.
الخطوة السابعة
الخطوة 7 احضر تقويم الإصدار لتحليل الموقع من قبل أيام الحدث
- ... /v1/calendar/{currency} تستخدمها يوم الاثنين لبناء مجموعة من تواريخ الإعلانات للأسبوع، ثم تخطي خطوة البحث في الأيام التي لا توجد فيها أحداث هذا يتجنب المكالمات غير الضرورية لـ API في الأسابيع الهادئة.
/**
* 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.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; fetget Date = set; fetchdate = set); fetchdata = set (date.getMacdate.getContent.getTexts); fetches date = loaddate.date (date); fetchecks date (date) = date (data); fetcheddate (data) = load (date).getData = load; fetches dates (date; date); fetcher = dates); fetchingdate (dates); fetchs date (dates) = days; date (day); date (dates); date; date;date); date = date; (date (day) = day); date); date of the week (date = day; date).We have a new release (date=today); date=date); (date-date); day (date/date);date); data (date), date (events); date) = dates (days); date-of-week (date)). (date_day); (day). (date)) (date of the day) (date): (dateof-day); {date); {data) {date_date_week); date_day) {day); } (date} (date{date_year); date). (dayday); day of the month (date}. (date date); (date) { (date } (day of the year); date), date = day (day; date) {date); date for today); date {date=day); if {date} day (d) {today) {if (day} (day (day), {date) {); date} (if (date {date).); date{day);
لاستخدام هذا النمط، تسجيل loadMacroDataCalendarAware كعامل الزناد بدلاً من loadMacroData استبدال سلسلة اسم الدالة في createWeekdayTrigger
وفقًا لذلك
الموجز
ملخص
لديك الآن خط أنابيب كامل جاهز للإنتاج يربط FXMacroData إلى Google Sheets عبر Apps Script:
- مساعدة استرجاع مع إعادة المحاولة والخلفية الأسية التي تتعامل مع أخطاء الشبكة العابرة واستجابات الحد السريع براقية.
- وظيفة تطبيعية تحويل كل رد الإعلان إلى صف جدول تبريدي ثابت وآمن من التكرار.
- كاتب ورقة يخلق رؤوس في أول تشغيل، يضيف إصدارات جديدة فقط، ويتخطى ما سبق رؤيته
announcement_datetimeالقيم - التشنج التكيفي للجملة المكثفة عبر عشرات أزواج العملات / المؤشرات.
- محرك تشغيل مدفوع بالوقت خلال أيام الأسبوع لتحديثات يومية آلية بالكامل
- فحص مسبق للتقويم الاختياري الذي يتجنب مكالمات API الزائدة في الأيام التي لا توجد فيها إصدارات مقررة.
الخطوات التالية
-
توسيع قائمة المؤشرات اطلع على الكتالوج الكامل على
/api-data-docs /أبي بيانات
وأضف أزواج ذات صلة باستراتيجيتك (مثل
chf/gov_bond_10y- لاeur/pmi- لاgbp/employment) -
إضافة تنسيق مشروط تبرز الصفوف حيث التوجيه هو
Beatباللون الأخضر وMissباللون الأحمرSpreadsheetApp- نعمConditionalFormatRuleBuilderلقراءة الإشارة في لمحة واحدة. -
إرسال التنبيهات إلى Slack أو البريد الإلكتروني بعد إضافة الصفوف، استخدم
MailApp.sendEmailأو مكالمة عبر الإنترنت بالداخلloadMacroDataلإعلام فريقك عندما تصل بصمة ذات تأثير كبير -
تتبع القيم التاريخية ال
الإعلانات نقطة النهاية
و أيضاً تقبل
start_date- لا ، لاend_dateمعايير تشغيل ملء مرة واحدة على نطاق تاريخ أطول لزرع علامة التبويب التاريخية جنبا إلى جنب مع تغذية حية.