MetalAPI
XAU3,997.89−1.54‎%‎XAG53.936−1.51‎%‎XPT1,554.47−0.83‎%‎XPD1,047.03−0.44‎%‎XCU6.5080+2.29‎%‎WTI103.27+3.82‎%‎BRENT111.70−1.30‎%‎EUR1.120400−0.01‎%‎GBP1.337500−0.01‎%‎JPY0.0063761+0.14‎%‎CHF1.213445−0.25‎%‎TRY0.020125−0.12‎%‎BTC80,329.75−0.55‎%‎ETH2,329.36−1.13‎%‎
جميع المقالات
الذهبالفضةالبيانات التاريخيةدرس تطبيقي

أسعار الذهب والفضة التاريخية عبر API: بيانات يومية على مدى 25 عامًا

احصل على أسعار الذهب والفضة التاريخية اليومية منذ عام 2000 عبر /historical و/timeframe و/change و/ohlc، وقسّم النطاقات الطويلة سنويًا، وخزّن البيانات وارسمها.

بقلم MetalAPI Team··وقت القراءة: 10 دقيقة

ما السجل التاريخي المتاح

تحتفظ MetalAPI بشريط يومي واحد لكل رمز في كل يوم، يتضمن سعر الافتتاح والأعلى والأدنى والإغلاق. وبالنسبة إلى الذهب والفضة يعني ذلك أكثر من 25 عامًا من البيانات اليومية: ما يكفي لرسم الاتجاهات طويلة الأجل، أو اختبار قاعدة تسعير على بيانات الماضي، أو إعادة تقييم فواتير قديمة بسعر يومها.

الفئةالرموزالسجل اليومي منذ
المعادن الثمينةXAU وXAG وXPT وXPD2000
المعادن الصناعية والطاقةXCU وALU وZNC وHRC وWTI وBRENT وغيرها2000
العملات57 عملة، منها USD وEUR وGBP وJPY1999-01-04
العملات المشفرة13 عملة، منها BTC وETH2014

يمكن لجميع الخطط الاستعلام عن السجل الكامل، ولا تختلف الخطط إلا في عدد الأيام التي يمكن أن يغطيها طلب نطاق واحد. تُكتب التواريخ بالصيغة YYYY-MM-DD بتوقيت UTC، وقيم كل يوم هي أسعار إغلاقه. ويُرجع التاريخ السابق لبداية سجل الرمز الخطأ 300 (no_results).

أسعار المعادن قبل أكتوبر 2026 مشتقة من العقود الآجلة للشهر الأقرب، لذا قد تختلف قليلًا عن السعر الفوري في اليوم نفسه. ضع ذلك في الحسبان عند مقارنة القيم القديمة بأسعار فورية من مصدر آخر.

تغطي أربع نقاط نهاية العمل مع البيانات التاريخية: /historical ليوم واحد، و/timeframe لنطاق زمني، و/change للتغيّر بين يومين، و/ohlc لشمعة يوم واحد. تتناولها الأقسام التالية واحدة تلو الأخرى، ثم تبني أرشيفًا محليًا صغيرًا ورسمًا بيانيًا فوقه.

يوم واحد: /historical

للحصول على أسعار الإغلاق ليوم واحد، ضع التاريخ في المسار. ويؤدي /historical?date=YYYY-MM-DD الغرض نفسه، أما /yesterday فاختصار لليوم السابق بتوقيت UTC:

bash
curl "https://metalapi.com/api/v1/2024-01-15?currencies=XAU,XAG" \
  -H "X-API-Key: $METALAPI_KEY"

للاستجابة بنية /latest نفسها، مع إضافة historical: true. وهذه هي القيم النموذجية من مرجع API:

مثال على الاستجابة
{
  "success": true,
  "timestamp": 1705276800,
  "historical": true,
  "date": "2024-01-15",
  "base": "USD",
  "rates": {
    "XAU": 0.0004874244,
    "USDXAU": 2051.6,
    "XAG": 0.0431741646,
    "USDXAG": 23.162
  }
}

USDXAU هو سعر أونصة تروي واحدة من الذهب بعملة الأساس، وXAU مقلوبه. ويعمل base وunit كالمعتاد، فيُرجع base=EUR&unit=gram سعر الغرام باليورو لذلك اليوم.

لا تُفشل عطلات نهاية الأسبوع والعطلات الرسمية الطلب: ففي اليوم الذي لا تداول فيه يُرجَع سعر إغلاق أحدث يوم تداول. ويبقى date في الاستجابة هو اليوم الذي طلبته، لذا إذا احتجت إلى معرفة ما إذا كانت الأسواق مفتوحة، فاحتفظ بتقويم تداول من جهتك. أما التاريخ المستقبلي فيُرجع الخطأ 205 (invalid_date).

النطاقات الزمنية: /timeframe

للحصول على سلسلة، تُرجع /timeframe إدخالًا واحدًا لكل يوم بين start_date وend_date شاملًا الطرفين، مفهرسًا بالتاريخ. أما /timeseries فاسم بديل يَسِم الاستجابة بـ timeseries: true بدلًا من timeframe: true.

bash
curl "https://metalapi.com/api/v1/timeframe?start_date=2026-10-19&end_date=2026-10-23&currencies=XAU" \
  -H "X-API-Key: $METALAPI_KEY"
مثال على الاستجابة
{
  "success": true,
  "timeframe": true,
  "start_date": "2026-10-19",
  "end_date": "2026-10-23",
  "base": "USD",
  "rates": {
    "2026-10-19": { "XAU": 0.0002403788, "USDXAU": 4160.1 },
    "2026-10-20": { "XAU": 0.0002413943, "USDXAU": 4142.6 },
    "2026-10-21": { "XAU": 0.0002409639, "USDXAU": 4150 },
    "2026-10-22": { "XAU": 0.0002462812, "USDXAU": 4060.4 },
    "2026-10-23": { "XAU": 0.0002495072, "USDXAU": 4007.9 }
  }
}

بخلاف نقطة نهاية اليوم الواحد، لا تملأ /timeframe الفجوات. فتداول المعادن الفورية يتوقف من الجمعة الساعة 17:00 حتى الأحد الساعة 17:00 بتوقيت نيويورك، لذا قد تغيب عطلات نهاية الأسبوع وعطلات السوق عن النتيجة. لا تفترض وجود إدخال لكل يوم تقويمي، وإذا جمعت رموزًا ذات تقاويم تداول مختلفة، كالذهب والبيتكوين، فتحقّق من وجود المفتاح قبل قراءته.

يعتمد أطول نطاق يمكن أن يغطيه طلب واحد على الخطة:

الخطةأطول نطاق لكل طلبوحدة تقسيم تناسب الحد دائمًا
Free30 يومًاشهر تقويمي واحد
Lite وما فوقها365 يومًاسنة تقويمية واحدة

الحد هو المسافة بين التاريخين، لذا فإن النطاق من 2025-01-01 إلى 2025-01-31 يناسب حد خطة Free، والنطاق من 2024-01-01 إلى 2024-12-31 يناسب حد الخطط المدفوعة حتى في سنة كبيسة. ويُرجع النطاق الأطول الخطأ 207 (timeframe_too_long) مع upgrade_url. ويُحتسب كل استدعاء لـ /timeframe طلبًا واحدًا، مهما كان عدد الأيام التي يُرجعها.

المقارنة بين تاريخين: /change

عندما لا تحتاج إلا إلى التغيّر بين يومين، كالأداء منذ بداية العام في لوحة معلومات، تُرجع /change سعر البداية وسعر النهاية والتغيّر المطلق والتغيّر بالنسبة المئوية دون تنزيل الأيام الواقعة بينهما:

bash
curl "https://metalapi.com/api/v1/change?start_date=2026-01-02&end_date=2026-10-23&currencies=XAU,XAG" \
  -H "X-API-Key: $METALAPI_KEY"
مثال على الاستجابة (الذهب فقط)
{
  "success": true,
  "change": true,
  "start_date": "2026-01-02",
  "end_date": "2026-10-23",
  "base": "USD",
  "rates": {
    "XAU": { "start_rate": 0.0002309682, "end_rate": 0.0002495072, "change": 0.000018539, "change_pct": 8.02665 },
    "USDXAU": { "start_rate": 4329.6, "end_rate": 4007.9, "change": -321.7, "change_pct": -7.43025 }
  }
}

اقرأ المفتاح ذا البادئة، USDXAU. فالمفتاح المقلوب XAU يتحرك في الاتجاه المعاكس، لذا تحمل قيمة change_pct فيه إشارة معاكسة وحجمًا مختلفًا: في هذا المثال انخفض سعر الذهب 7.43% بينما ارتفع المقلوب 8.03%. وعرض النسبة المئوية للمقلوب على أنها تغيّر السعر خطأ يسهل أن يتسلل إلى بيئة الإنتاج.

لـ /change حدود النطاق نفسها التي لـ /timeframe. وكما في /historical، تستخدم أحدث سعر إغلاق عندما يقع التاريخ في عطلة نهاية الأسبوع أو عطلة رسمية.

الشموع اليومية: /ohlc

تُرجع /ohlc سعر الافتتاح والأعلى والأدنى والإغلاق ليوم واحد، في الاتجاهين: USDXAU بالدولار للأونصة، وXAU كمقلوب. ومن دون date، أو مع date=latest، تُرجع شريط اليوم حتى اللحظة.

bash
curl "https://metalapi.com/api/v1/ohlc?date=2026-10-23&currencies=XAU" \
  -H "X-API-Key: $METALAPI_KEY"
مثال على الاستجابة
{
  "success": true,
  "ohlc": true,
  "date": "2026-10-23",
  "base": "USD",
  "rates": {
    "USDXAU": { "open": 4042.2, "high": 4050.2, "low": 3989.1, "close": 4007.9 },
    "XAU": { "open": 0.000247390, "high": 0.000250683, "low": 0.000246901, "close": 0.000249507 }
  }
}

لاحظ أن الأعلى في كتلة المقلوب يأتي من أدنى سعر، والأدنى من أعلى سعر، لأن القلب يعكس الترتيب. لذا ارسم كتلة USDXAU.

يغطي كل استدعاء يومًا واحدًا، لذا يتطلب رسم شموع سنة كاملة طلبًا لكل يوم تداول. أما للرسوم البيانية الطويلة فأسعار الإغلاق من /timeframe أرخص بكثير. استخدم /ohlc للشموع الحديثة أو لعرض نطاق تداول اليوم بجوار السعر الحالي.

وللحصول على شموع أسبوعية أو شهرية على مدى سنوات كثيرة، يمكنك أيضًا بناء الأشرطة من أسعار الإغلاق اليومية المخزَّنة: الافتتاح هو أول سعر إغلاق في الفترة، والإغلاق هو آخرها، والأعلى والأدنى هما أعلى أسعار الإغلاق وأدناها. صحيح أنها لا تلتقط القمم والقيعان خلال اليوم، لذا صِفها بأنها مبنية على أسعار الإغلاق، لكنها مناسبة للرسوم البيانية طويلة المدى.

تعبئة 25 عامًا من البيانات، سنة في كل طلب

مع حد 365 يومًا لكل طلب، يتطلب سجل الذهب والفضة الكامل منذ عام 2000 استدعاءً واحدًا لـ /timeframe لكل سنة تقويمية: 27 طلبًا للأعوام من 2000 حتى 2026. وهذا يتسع بسهولة ضمن الحصة الشهرية لأي خطة مدفوعة. أما في خطة Free، فتحتاج نوافذ الثلاثين يومًا إلى أكثر من 300 طلب، أي نحو ثلاثة أضعاف حصتها الشهرية البالغة 100 طلب، لذا لا تكون التعبئة الكاملة عملية هناك، وإن كانت بضعة أشهر من السجل ممكنة.

يقسّم سكربت Python التالي الطلبات حسب السنة، وينتظر انقضاء حد الطلبات في الدقيقة، ويكتب مفاتيح أسعار المعدنين في SQLite. احفظه باسم backfill.py:

backfill.py
import os
import sqlite3
import time
from datetime import datetime, timezone

import requests

API = "https://metalapi.com/api/v1"
session = requests.Session()
session.headers["X-API-Key"] = os.environ["METALAPI_KEY"]

db = sqlite3.connect("metals.db")
db.execute(
    """create table if not exists daily_close (
         day    text not null,
         symbol text not null,
         base   text not null,
         price  real not null,
         primary key (day, symbol, base)
       )"""
)


def fetch_timeframe(start, end, symbols, base="USD"):
    """Daily closes between start and end (inclusive), keyed by date."""
    while True:
        r = session.get(
            f"{API}/timeframe",
            params={"start_date": start, "end_date": end, "currencies": ",".join(symbols), "base": base},
            timeout=30,
        )
        data = r.json()
        if data.get("success"):
            return data["rates"]
        err = data["error"]
        if err["code"] == 104:  # per-minute limit: wait, then retry (not billed)
            time.sleep(int(r.headers.get("Retry-After", "60")))
            continue
        if err["code"] == 300:  # no data in this window
            return {}
        raise RuntimeError(f"{err['code']} {err['type']}: {err['info']}")


def store(rates, symbols, base="USD"):
    rows = [
        (day, s, base, values[f"{base}{s}"])
        for day, values in rates.items()
        for s in symbols
        if f"{base}{s}" in values
    ]
    db.executemany("insert or replace into daily_close values (?, ?, ?, ?)", rows)
    db.commit()
    return len(rows)


def backfill(first_year, symbols=("XAU", "XAG"), base="USD"):
    today = datetime.now(timezone.utc).date()
    for year in range(first_year, today.year + 1):
        start = f"{year}-01-01"
        end = min(f"{year}-12-31", today.isoformat())
        n = store(fetch_timeframe(start, end, symbols, base), symbols, base)
        print(f"{year}: {n} rows")


if __name__ == "__main__":
    backfill(2000)

يمكن إعادة تشغيل السكربت بأمان: إذ يستبدل insert or replace الصفوف الموجودة، فيمكن ببساطة إعادة تشغيل تعبئة انقطعت. وتنتهي السنة الحالية عند تاريخ اليوم بتوقيت UTC، لأن قيمة end_date المستقبلية تُرجع 205. وحدود الطلبات في الدقيقة هي الأدنى في الخطط الأصغر (10 طلبات في الدقيقة في Free و30 في Lite)، ولهذا يلتزم السكربت بقيمة Retry-After عند الخطأ 104 بدلًا من التوقف.

يخزّن السكربت USDXAU وUSDXAG، أي الأسعار، لا المفاتيح المقلوبة. وإذا احتجت إلى السجل بعملة أخرى، فمرّر base، أو خزّن أسعار العملات بالدولار أيضًا وحوّل محليًا من صفوف اليوم نفسه.

تخزين البيانات وتحديثها

أسعار إغلاق الأيام الماضية لا تتغير، لذا فإن النسخة المحلية هي الطريقة الأرخص والأسرع لتقديم الرسوم البيانية والتقارير. وتحافظ بضع قواعد على صحتها:

  • فهرس الصفوف باليوم والرمز وعملة الأساس. يستخدم الجدول أعلاه (day, symbol, base) مفتاحًا أساسيًا، لذا فإن جلب النطاق نفسه مرتين يستبدل الصفوف بدلًا من تكرارها.
  • سجّل ما طلبته. خزّن base، وunit إن كنت تستخدمه، مع البيانات. فخلط صفوف بالغرام وأخرى بالأونصة في عمود واحد سبب كلاسيكي للقفزات المفاجئة في الرسم البياني.
  • حدّث مرة واحدة يوميًا. بعد التعبئة، يكفي طلب واحد يوميًا لإبقاء الجدول محدّثًا. اجلب آخر سبعة أيام، حتى يُستكمل تلقائيًا أي تشغيل فائت أو سعر إغلاق متأخر.
  • عامل اليوم الحالي على أنه مؤقت. شريط اليوم الحالي وحده لا يزال يتحرك. أبقِه خارج الأرشيف، أو استبدله في التشغيل التالي كما يفعل السكربت أدناه.
daily.py (يُشغَّل مرة يوميًا بعد 00:00 UTC)
from datetime import datetime, timedelta, timezone

from backfill import fetch_timeframe, store

SYMBOLS = ("XAU", "XAG")
today = datetime.now(timezone.utc).date()
start = (today - timedelta(days=7)).isoformat()
end = (today - timedelta(days=1)).isoformat()
print(store(fetch_timeframe(start, end, SYMBOLS), SYMBOLS), "rows updated")

بمعدل طلب واحد يوميًا، يكلّف التحديث نحو 30 طلبًا شهريًا، وهو ما يترك هامشًا حتى في خطة Free. وللأرشيفات الأكبر، يعمل النمط نفسه مع PostgreSQL أو ملف Parquet أو أي قاعدة بيانات للسلاسل الزمنية، ولا يتغير سوى الدالة store.

قبل أن تبني أي شيء على بيانات التعبئة، تحقّق منها. ثلاثة فحوص تكشف معظم المشكلات:

  • عدد الصفوف لكل سنة. يجب أن تحتوي كل سنة مكتملة على صف واحد تقريبًا لكل يوم تداول لكل معدن. والسنة التي فيها صفوف أقل بكثير تعني أن طلبًا فشل أو لم يُرجع بيانات.
  • الفجوات. تترك عطلة نهاية الأسبوع العادية ثلاثة أيام بين سعري إغلاق، وعطلة نهاية الأسبوع الطويلة أربعة أيام. وأي فجوة أطول تستحق الفحص، وإعادة جلب تلك النافذة لا تكلّف سوى طلب واحد.
  • القفزات. التغيّر من يوم إلى آخر بمعامل يقارب 31 أو 1,000 يشير إلى وحدات مختلطة، والقيمة القريبة من الصفر تشير إلى تسرّب مفتاح مقلوب.

الفحصان الأولان استعلامان قصيران في SQLite:

checks.sql
-- Rows per year: a complete year has roughly one row per trading day
select substr(day, 1, 4) as year, symbol, count(*) as days from daily_close
 where base = 'USD' group by year, symbol order by year, symbol;

-- Gaps of more than four days between stored closes
select symbol, prev_day, day from (
  select symbol, day, lag(day) over (partition by symbol order by day) as prev_day
    from daily_close where base = 'USD'
) where julianday(day) - julianday(prev_day) > 4;

رسم البيانات بيانيًا

مع تخزين البيانات محليًا، لا تكلّف الرسوم البيانية أي طلبات. يرسم سكربت matplotlib التالي سعر الذهب بالدولار مع نسبة الذهب إلى الفضة، أي عدد أونصات الفضة التي تشتريها أونصة واحدة من الذهب:

chart.py
import sqlite3
from datetime import date

import matplotlib.dates as mdates
import matplotlib.pyplot as plt

db = sqlite3.connect("metals.db")
rows = db.execute(
    """select g.day, g.price, s.price
         from daily_close g
         join daily_close s on s.day = g.day and s.base = g.base and s.symbol = 'XAG'
        where g.symbol = 'XAU' and g.base = 'USD'
        order by g.day"""
).fetchall()

days = [date.fromisoformat(day) for day, _, _ in rows]
gold = [g for _, g, _ in rows]
ratio = [g / s for _, g, s in rows]

fig, (top, bottom) = plt.subplots(2, 1, sharex=True, figsize=(10, 6))
top.plot(days, gold, linewidth=1)
top.set_ylabel("Gold, USD per troy oz")
bottom.plot(days, ratio, linewidth=1)
bottom.set_ylabel("Gold/silver ratio")
bottom.xaxis.set_major_locator(mdates.YearLocator(base=5))
fig.tight_layout()
fig.savefig("gold-silver.png", dpi=150)

على موقع ويب، قدّم السلسلة المخزَّنة من واجهتك الخلفية وارسمها في المتصفح، فيبقى مفتاح API على الخادم ولا تستهلك مشاهدات الصفحة حصتك. وهذه صفحة Chart.js بسيطة تقرأ مصفوفة JSON من كائنات { day, price } من نقطة النهاية الخاصة بك:

gold-chart.html
<canvas id="gold" height="320"></canvas>
<script src="https://cdn.jsdelivr.net/npm/[email protected]/dist/chart.umd.js"></script>
<script>
  // Your own endpoint, returning [{ "day": "2026-10-23", "price": 4007.9 }, ...]
  fetch("/api/gold-history")
    .then((res) => res.json())
    .then((points) => {
      new Chart(document.getElementById("gold"), {
        type: "line",
        data: {
          labels: points.map((p) => p.day),
          datasets: [{ label: "Gold, USD per troy oz", data: points.map((p) => p.price), pointRadius: 0, borderWidth: 1 }],
        },
        options: {
          animation: false,
          scales: { x: { ticks: { maxTicksLimit: 10 } } },
        },
      });
    });
</script>

خمسة وعشرون عامًا من أيام التداول تعني أكثر من 6,000 نقطة لكل سلسلة. ويحافظ إيقاف علامات النقاط والحركة، كما في المثال أعلاه، على سرعة استجابة الرسم البياني، وعلى الشاشات الصغيرة فكّر في تقليل العينات إلى أسعار الإغلاق الأسبوعية.

يتضمن دليل البيانات التاريخية دالة مساعدة للنطاقات بلغة JavaScript ومثالًا على المتوسط المتحرك، ويسرد توثيق API جميع المعاملات. وإذا كنت تعمل مع مساعدي الذكاء الاصطناعي، فبإمكان خادم MetalAPI MCP جلب السلاسل الزمنية نفسها من داخل المحادثة. وللمفاتيح والأخطاء والتخزين المؤقت عمومًا، ابدأ بـ دليل واجهة API لأسعار الذهب، وقارن حدود النطاقات والحصص في صفحة الأسعار.

جرّبه مع بياناتك الخاصة

أنشئ حسابًا مجانيًا وأرسل طلبك الأول في أقل من دقيقتين. 100 طلب شهريًا، دون بطاقة ائتمان.

تابع القراءة