استعلم عن بيانات Narwhal باستخدام DuckDB API

يمكنك استخدام DuckDB مع Narwhal API للاستعلام بلغة SQL عن الإجازات الرسمية ومواعيد الإصدار والبيانات الاقتصادية والسلع وأسعار الصرف واحتمالات أسواق التنبؤ. تجلب دالة Python صغيرة ملف JSON بمفتاح API الخاص بك وتحفظ كل استجابة في ملف، ثم يقرأه DuckDB إلى جداول.

ما الذي ستعدّه

يقرأ DuckDB ملفات JSON المحلية بالدالة read_json، وهذا الإعداد يمنحك قاعدة بيانات narwhal.duckdb فيها جدول لكل مجال من مجالات البيانات. يتولى Python طلبات API ويكتب ملفات الاستجابة التي تقرؤها SQL. يتضمن المثال الكامل استعلامات عن أعداد الإجازات ومواعيد الإصدار، وملاحظات CPI، وأسعار Brent، وأسعار الصرف، واحتمالات سياسة الاحتياطي الفيدرالي. يستخدم Narwhal حسابًا واحدًا ومفتاح API واحدًا لهذه الواجهات، ويشترك REST وMCP في هذا المفتاح.

اضبط مفتاح API الخاص بك

  1. أنشئ مفتاحًا مجانيًا أنشئ مفتاحك المجاني من narwhalapi.com/signup. استخدم مفتاح Narwhal API نفسه لمجالات البيانات في المثال؛ وترسله دالة الطلب المساعدة في ترويسة Authorization بالصيغة Bearer YOUR_KEY.
  2. احفظ المفتاح في متغير بيئة عيّن متغير بيئة باسم NARWHAL_API_KEY لملف Python. يقرؤه إعداد Python عبر os.environ ويضعه في الترويسات التي تستخدمها fetch، فلا يظهر المفتاح في نص SQL.
  3. ثبّت حزم Python ثبّت duckdb وrequests لملف Python. يستورد المثال الحزمتين، ويستخدم requests لجلب كل استجابة، ويفتح قاعدة البيانات بـ duckdb.connect.
    ثبّت DuckDB وrequests
    pip install duckdb requests

اسحب البيانات بالكود

احفظ مثال Python في ملف وشغّله مع إتاحة NARWHAL_API_KEY في بيئته. يكتب كل استدعاء لـ fetch ملف JSON باسم محدد، ثم تقرأ SQL التالية ذلك الملف إلى الجدول المقابل. ترسل الدالة المساعدة طلبات GET إلى https://api.narwhalapi.com وتستدعي raise_for_status قبل كتابة الاستجابة. وتقرأ الاستعلامات الأخيرة الجداول الناتجة عبر اتصال DuckDB نفسه. استخدم الأمثلة الأقصر لكل مجال مع هذه الدالة المساعدة والاتصال عندما تريد سحب مجال واحد.

DuckDB: ستة مجالات في جداول
import os
import duckdb
import requests

API = "https://api.narwhalapi.com"
HEADERS = {"Authorization": f"Bearer {os.environ['NARWHAL_API_KEY']}"}


def fetch(name, path, **params):
    r = requests.get(API + path, params=params, headers=HEADERS, timeout=10)
    r.raise_for_status()
    with open(f"{name}.json", "w") as file:
        file.write(r.text)


con = duckdb.connect("narwhal.duckdb")

fetch("holidays", "/v1/calendars/holidays/USA/2026")
con.sql(
    "CREATE OR REPLACE TABLE holidays AS "
    "SELECT unnest(holidays, recursive := true) "
    "FROM read_json('holidays.json')"
)

fetch(
    "releases",
    "/v1/economics/calendar",
    countries="USA",
    start_date="2026-11-01",
    end_date="2026-11-30",
    limit=100,
)
con.sql(
    "CREATE OR REPLACE TABLE releases AS "
    "SELECT unnest(releases, recursive := true) "
    "FROM read_json('releases.json')"
)

fetch("cpi", "/v1/economics/USA/cpi/observations", start="2025-09")
con.sql(
    "CREATE OR REPLACE TABLE cpi AS "
    "SELECT unnest(observations, recursive := true) "
    "FROM read_json('cpi.json')"
)

fetch("brent", "/v1/commodities/spot/BRENT/USD")
con.sql(
    "CREATE OR REPLACE TABLE brent AS SELECT price, unit FROM read_json('brent.json')"
)

fetch("fx", "/v1/fx/rates/USD", currencies="EUR,IDR")
con.sql(
    "CREATE OR REPLACE TABLE fx AS "
    "SELECT unnest(rates, recursive := true) "
    "FROM read_json('fx.json')"
)

fetch("fed", "/v1/predictions/policy-paths/federal-reserve")
con.sql(
    "CREATE OR REPLACE TABLE fed AS "
    "SELECT unnest(meetings, recursive := true) "
    "FROM read_json('fed.json')"
)

print(
    con.sql("SELECT count(*) FROM holidays").fetchall(),
    con.sql("SELECT count(*) FROM releases").fetchall(),
)
print(con.sql("SELECT period, value FROM cpi ORDER BY period DESC LIMIT 2").fetchall())
print(
    con.sql("SELECT * FROM brent").fetchall(),
    con.sql("SELECT currency, rate FROM fx").fetchall(),
)
print(
    con.sql(
        "SELECT p_hold, expected_change_bps FROM fed ORDER BY resolves_at LIMIT 1"
    ).fetchall()
)

شغّله وفق جدول

شغّل ملف Python من cron لتكرار السحب. اجعل NARWHAL_API_KEY متاحًا لملف Python المجدول، لأن ترويسات الطلب تقرأ المفتاح من البيئة. يجلب كل استدعاء استجابته من جديد ويكتب ملف JSON الخاص به قبل أن يقرأه DuckDB. يجعل CREATE OR REPLACE TABLE كل تشغيل قابلًا للتكرار: إذ تتلقى الجداول بالأسماء نفسها البيانات المقروءة من تلك الملفات. وفي السحب المجدول، أبقِ استدعاءات fetch وعبارات إنشاء الجداول معًا في ملف Python نفسه.

الإجازات الرسمية في DuckDB

اجلب تقويم الإجازات الرسمية للولايات المتحدة لعام 2026، ثم اقرأ حقل holidays منه إلى جدول holidays. يختار المثال date وname ليحتوي ناتج SQL على تواريخ الإجازات مع أسمائها. في 24 أكتوبر 2026 احتوت هذه الاستجابة على 11 إجازة. ويتضمن مثال Python الكامل أيضًا استعلام عدّ لهذا الجدول إن أردت العدد بدلًا من صفوف الإجازات.

اقرأ تواريخ الإجازات وأسماءها
fetch("holidays", "/v1/calendars/holidays/USA/2026")
con.sql(
    "CREATE OR REPLACE TABLE holidays AS "
    "SELECT unnest(holidays, recursive := true) "
    "FROM read_json('holidays.json')"
)
print(con.sql("SELECT date, name FROM holidays").fetchall())

مواعيد الإصدار في DuckDB

اسحب التقويم الاقتصادي للولايات المتحدة لشهر نوفمبر 2026 بتاريخ البداية وتاريخ النهاية والحد الأقصى المحددة. يحتوي حقل releases في الاستجابة على at وperiod وtopic وstatus، ويقرأ هذا المثال أعمدة الفترة والموضوع والحالة من الجدول. في 24 أكتوبر 2026 احتوت الاستجابة على 31 إصدارًا. هذه مواعيد إصدار اقتصادية، أما طلب الملاحظات في المثال التالي فيقرأ قيم CPI بحسب الفترة.

اقرأ فترات الإصدار وموضوعاتها
fetch(
    "releases",
    "/v1/economics/calendar",
    countries="USA",
    start_date="2026-11-01",
    end_date="2026-11-30",
    limit=100,
)
con.sql(
    "CREATE OR REPLACE TABLE releases AS "
    "SELECT unnest(releases, recursive := true) "
    "FROM read_json('releases.json')"
)
print(con.sql("SELECT period, topic, status FROM releases").fetchall())

البيانات الاقتصادية في DuckDB

اقرأ ملاحظات CPI للولايات المتحدة مع ضبط start على 2025-09. اسم حقل JSON هو observations، ويحتوي جدول cpi الناتج على العمودين period وvalue ليختارهما الاستعلام. في 24 أكتوبر 2026 كانت قيمة CPI لشهر 2026-09 تساوي 336.940. يرتّب المثال الملاحظات بحسب الفترة تنازليًا ويحدّ النتيجة، باستخدام الاستعلام نفسه الموجود في مثال Python الكامل.

اقرأ ملاحظات CPI
fetch("cpi", "/v1/economics/USA/cpi/observations", start="2025-09")
con.sql(
    "CREATE OR REPLACE TABLE cpi AS "
    "SELECT unnest(observations, recursive := true) "
    "FROM read_json('cpi.json')"
)
print(con.sql("SELECT period, value FROM cpi ORDER BY period DESC LIMIT 2").fetchall())

السلع في DuckDB

اجلب سعر Brent الفوري بالدولار الأمريكي واقرأ price وunit مباشرةً من ملف JSON المحفوظ. تختار عبارة إنشاء الجدول هذه الحقلين دون توسيع أي قائمة. في 24 أكتوبر 2026 كان سعر Brent يساوي 116.8809 دولارًا للبرميل. يقرأ المثال جدول brent بعبارة SELECT *، فيتوفر السعر ووحدته معًا في النتيجة العائدة إلى Python.

اقرأ سعر Brent ووحدته
fetch("brent", "/v1/commodities/spot/BRENT/USD")
con.sql(
    "CREATE OR REPLACE TABLE brent AS SELECT price, unit FROM read_json('brent.json')"
)
print(con.sql("SELECT * FROM brent").fetchall())

أسعار الصرف في DuckDB

استخدم USD عملةً أساسية واطلب EUR وIDR في المعامل currencies. اقرأ حقل rates من الاستجابة إلى جدول fx، ثم اختر currency وrate معًا. في 24 أكتوبر 2026 كان USD/EUR يساوي 0.89271 وUSD/IDR يساوي 18107.8. تُخزَّن هذه الأسعار كسلاسل نصية. ويُبقي الاستعلام كل عملة بجانب سعرها عند قراءة الصفوف إلى Python.

اقرأ أسعار الصرف
fetch("fx", "/v1/fx/rates/USD", currencies="EUR,IDR")
con.sql(
    "CREATE OR REPLACE TABLE fx AS "
    "SELECT unnest(rates, recursive := true) "
    "FROM read_json('fx.json')"
)
print(con.sql("SELECT currency, rate FROM fx").fetchall())

أسواق التنبؤ في DuckDB

اقرأ مسار سياسة الاحتياطي الفيدرالي من حقل meetings في الاستجابة إلى جدول fed. في 24 أكتوبر 2026 كان احتمال التثبيت في اجتماع 28 أكتوبر 2026 يساوي 0.9188، مع تغيير متوقع قدره 1.7 نقطة أساس. يرتّب الاستعلام بحسب resolves_at ويقرأ p_hold وexpected_change_bps لذلك الاجتماع، ويعيد الأعمدة الرقمية إلى Python.

اقرأ احتمال التثبيت والتغيير المتوقع
fetch("fed", "/v1/predictions/policy-paths/federal-reserve")
con.sql(
    "CREATE OR REPLACE TABLE fed AS "
    "SELECT unnest(meetings, recursive := true) "
    "FROM read_json('fed.json')"
)
print(
    con.sql(
        "SELECT p_hold, expected_change_bps FROM fed ORDER BY resolves_at LIMIT 1"
    ).fetchall()
)

لماذا أستخدم unnest مع حقول القوائم؟

تقع القائمة داخل حقل JSON واحد. يحوّلها unnest(list_column, recursive := true) إلى صف لكل عنصر، فتصبح قوائم الإجازات والإصدارات والملاحظات والأسعار والاجتماعات صفوفًا في جداولها. استخدم اسم حقل الاستجابة داخل unnest: فاستجابة CPI مثلًا تستخدم observations رغم أن الملف المحفوظ والجدول يحملان الاسم cpi.

كيف أختار الطوابع الزمنية عبر Python؟

ثبّت pytz قبل اختيار الطوابع الزمنية at أو resolves_at عبر Python. ويمكنك أيضًا تحويل نوع الطوابع الزمنية في SQL عند الحاجة. يرتّب مثال التنبؤ بحسب resolves_at داخل SQL ويعيد الأعمدة الرقمية إلى Python، بينما يختار مثال الإصدار period وtopic وstatus.

نُشر في .

الخطوات التالية