العودة إلى المدونة

تحويل العملات في Power BI: أسعار صرف حية عبر واجهة برمجية (دليل 2026)

V
Vlado Grigirov
August 27, 2026
Currency API Exchange Rates Power BI Power Query DAX Tutorial Finexly

كل دليل عن تحويل العملات في Power BI ينتمي إلى أحد معسكرين. معسكر DAX يعرض عليك مقياساً أنيقاً ويفترض بهدوء أن جدول ExchangeRate موجود أصلاً في نموذجك. ومعسكر واجهات البرمجة يعرض عليك مقطعاً من Power Query يعمل ببراعة في Power BI Desktop ثم ينهار لحظة النشر، لأن التحديث المجدول يرفضه.

هذا الدليل يغطي النصفين معاً وبالترتيب: كيف تسحب أسعار صرف حية إلى Power BI من واجهة REST بطريقة تنجو من النشر على خدمة Power BI، وكيف تصمّم نموذج هذه الأسعار حتى تكون أرقامك قابلة للدفاع عنها، وكيف تحوّل المبالغ إمّا وقت الاستيراد أو وقت الاستعلام بحسب ما يحتاجه تقريرك فعلاً.

كل أمثلة Power Query وDAX أدناه كُتبت وفق أشكال الاستجابة الموثّقة في واجهة Finexly البرمجية.

حدّد أولاً أي مشكلة تحويل لديك

«تحويل العملات في Power BI» هو ثلاث مشكلات هندسية مختلفة تحمل الاسم نفسه، واختيار الخطأ منها هو أغلى غلطة في هذا المقال كله.

  1. عملات مصدر متعددة، عملة تقارير واحدة. جدول المبيعات فيه صفوف باليورو والجنيه والين، والمدير المالي يريد رقماً واحداً بالدولار. حوّل وقت الاستيراد. السعر خاصية للمعاملة لا للتقرير.
  2. عملة مصدر واحدة، عملات تقارير متعددة. كل شيء مخزّن بالدولار والمستخدم يختار عملة العرض من مقسّم شرائح. حوّل وقت الاستعلام باستخدام DAX. حساب كل عملة مسبقاً غير عملي.
  3. عملات مصدر متعددة وعملات تقارير متعددة. وحّد كل شيء إلى عملة محورية واحدة وقت الاستيراد، ثم طبّق الحالة الثانية فوقها. لا تحاول حلّها في خطوة واحدة.

القاعدة التي يكررها المصممون المخضرمون، وتستحق التكرار مرة أخرى: حوّل في أبكر وقت تستطيعه. كل تحويل تؤجله إلى وقت الاستعلام يكلّفك عند كل عنصر مرئي، وكل تغيير في المرشّح، وكل نقرة على مقسّم الشرائح.

سحب الأسعار الحية إلى Power Query بالطريقة الصحيحة

ابدأ بجدول الأسعار. أكفأ استدعاء هو استعلام متعدد الأزواج، فتحصل على كل العملات التي تهمّك في طلب واحد بدل طلب لكل عملة.

curl -H "Authorization: Bearer YOUR_API_KEY" \
  "https://api.finexly.com/v1/convert?q=USD_EUR,USD_GBP,USD_JPY"
{
  "USD_EUR": { "rate": 0.9215 },
  "USD_GBP": { "rate": 0.7892 }
}

وهذا ما يقابله كاستعلام M في Power Query. أنشئ استعلاماً فارغاً (الصفحة الرئيسية ← مصدر جديد ← استعلام فارغ ← المحرر المتقدم) والصق:

let
    ApiKey = "YOUR_API_KEY",
    Pairs  = "USD_EUR,USD_GBP,USD_JPY,USD_CAD,USD_AUD,USD_CHF",
    Source = Json.Document(
        Web.Contents(
            "https://api.finexly.com",
            [
                RelativePath = "v1/convert",
                Query        = [ q = Pairs ],
                Headers      = [ #"Authorization" = "Bearer " & ApiKey ]
            ]
        )
    ),
    ToTable  = Record.ToTable(Source),
    Expanded = Table.ExpandRecordColumn(ToTable, "Value", {"rate"}, {"Rate"}),
    Split    = Table.SplitColumn(
                   Expanded, "Name",
                   Splitter.SplitTextByDelimiter("_", QuoteStyle.None),
                   {"BaseCurrency", "Currency"}
               ),
    Typed    = Table.TransformColumnTypes(
                   Split,
                   {{"BaseCurrency", type text}, {"Currency", type text}, {"Rate", type number}}
               ),
    Stamped  = Table.AddColumn(Typed, "RetrievedAt", each DateTimeZone.UtcNow(), type datetimezone)
in
    Stamped

سمّه FxRates. ستحصل على جدول من أربعة أعمدة — BaseCurrency وCurrency وRate وRetrievedAt — جاهز للاستخدام في العلاقات والمقاييس.

خطأ Web.Contents الذي يعطّل التحديث المجدول

انتبه لما لا يفعله الاستعلام أعلاه: هو لا يدمج المعاملات داخل نص عنوان URL أبداً. هذا هو السبب الأكثر شيوعاً لأن يعمل تقرير تحويل العملات على سطح المكتب ويموت في السحابة.

لو كتبت هذا بدلاً منه:

// Do NOT do this
Source = Json.Document(
    Web.Contents("https://api.finexly.com/v1/convert?q=" & Pairs)
)

…سيحدّثه Power BI Desktop دون اعتراض، وسترفضه خدمة Power BI برسالة «تتضمن مجموعة البيانات هذه مصدر بيانات ديناميكياً. التحديث غير مدعوم.» تحتاج الخدمة إلى تحديد عنوان URL أساسي ثابت وقت التحليل كي تربط به بيانات الاعتماد. تمرير الأجزاء المتغيرة عبر RelativePath وQuery يمنحها ذلك بالضبط: يبقى الأساس https://api.finexly.com ويعيش كل ما هو ديناميكي داخل الخيارات.

الأمر نفسه ينطبق على أي استعلام يبني عنواناً من معامل أو تاريخ أو قيمة من جدول آخر. إن خرجت من هذا المقال بشيء واحد، فليكن RelativePath.

التعامل مع مفتاح الواجهة دون تضمينه في الشيفرة

كتابة ApiKey = "YOUR_API_KEY" مباشرة مقبولة في اختبار من خمس دقائق وخاطئة في أي شيء مشترك. شيفرة M لنموذج دلالي مرئية لكل من يملك إذن الإنشاء عليه.

خياران عمليان:

  • معامل Power Query (الصفحة الرئيسية ← إدارة المعاملات)، يُشار إليه بـ ApiKey = KeyParam. يظل مخزّناً مع النموذج، لكنه مركزي وسهل التدوير، ويمكن تجاوزه لكل بيئة عبر مسارات النشر.
  • نوع بيانات اعتماد Web API. في خدمة Power BI، اذهب إلى إعدادات النموذج الدلالي ← بيانات اعتماد مصدر البيانات ← تحرير بيانات الاعتماد واختر Web API وضع المفتاح هناك. عندها تحقن الخدمة ترويسة Authorization بنفسها ويمكنك حذف خيار Headers من شيفرة M نهائياً. هكذا يبقى السر خارج تعريف النموذج.

أياً كان اختيارك، استخدم مفتاحاً مقصوراً على أغراض التقارير، ودوّره عند مغادرة أحد أفراد الفريق. إن انتهى المفتاح داخل سلسلة استعلام العنوان بدل الترويسة فقد يظهر في سجلات الوصول للوكلاء والخوادم — وثائق Finexly تنبّه إلى ذلك، وهو ينطبق على كل مزوّد.

ابنِ بُعداً للعملات لا مجرد قائمة أسعار

جدول الأسعار وحده يعطيك أرقاماً. لا يعطيك تسميات صحيحة ولا خانات عشرية صحيحة ولا مقسّم شرائح يُرتَّب بشكل منطقي. اسحب قائمة العملات كبُعد مستقل:

let
    ApiKey = "YOUR_API_KEY",
    Source = Json.Document(
        Web.Contents(
            "https://api.finexly.com",
            [
                RelativePath = "v1/currencies",
                Headers      = [ #"Authorization" = "Bearer " & ApiKey ]
            ]
        )
    ),
    ToTable = Table.FromList(Source, Splitter.SplitByNothing(), {"Currency"}),
    Typed   = Table.TransformColumnTypes(ToTable, {{"Currency", type text}})
in
    Typed

ثم أضف العمودين اللذين لا يستطيع Power BI استنتاجهما نيابة عنك:

  • MinorUnits — عدد الخانات العشرية التي تستخدمها العملة فعلاً. الين صفر، والدينار الكويتي ثلاثة، ومعظم العملات اثنتان. تنسيق إجمالي بالين بخانتين عشريتين خطأ ظاهر في تقرير مالي، والتقريب في الخطوة الخاطئة يضاعفه. دليل تقريب العملات يوضح من أين يتسلل الخطأ.
  • FormatString — مثل "\€#,0.00" و"\¥#,0". ستحتاجه لاحقاً لسلاسل التنسيق الديناميكية.

كلاهما يتبع معيار ISO 4217 لا شيئاً يعرفه Power BI أصلاً؛ ومرجع ISO 4217 يتضمن الجدول الكامل. علّم هذا الجدول كبُعد، واربطه بـ FxRates[Currency] بعلاقة واحد إلى متعدد، واستخدمه هو — لا جدول الأسعار — مصدراً لمقسّم الشرائح.

المسار أ: التحويل وقت الاستيراد (سريع، ممل، صحيح)

في السيناريو الأول — عملات كثيرة داخلة وعملة واحدة خارجة — أنجز العمل في Power Query ودع النموذج يخزّن رقماً نظيفاً واحداً.

  1. حمّل استعلام المعاملات إلى Power Query.
  2. الصفحة الرئيسية ← دمج الاستعلامات، بربط Transactions[Currency] بـ FxRates[Currency] (خارجي أيسر).
  3. وسّع العمود المدمج واحتفظ بـ Rate.
  4. إضافة عمود ← عمود مخصص:
= if [Currency] = "USD" then [Amount]
  else if [Rate] = null then null
  else [Amount] / [Rate]

انتبه لفرع null. الربط الخارجي الأيسر بجدول أسعار تنقصه عملة يُنتج null، وnull في حساب Power Query يُنتج null بصمت بدل أن يرفع خطأ — فيتحول إلى فراغ في العنصر المرئي وإلى إجمالي أقل من الصحيح دون أن يلاحظ أحد. اجعل الفجوة صريحة كي تستطيع تصفيتها ورؤيتها.

وانتبه أيضاً للقسمة. USD_EUR = 0.9215 تعني أن دولاراً واحداً يشتري 0.9215 يورو، لذا تحويل مبلغ باليورو إلى الدولار يكون بالقسمة، وتحويل مبلغ بالدولار إلى اليورو يكون بالضرب. عكس الاتجاه هو ثاني أشيع خطأ في تقارير تعدد العملات، وعند أسعار قريبة من 1.0 يكاد يكون غير مرئي: خطأ بنسبة 3% في رقم يورو/دولار يبدو كفرق تقريب حتى يبدأ أحدهم بالمطابقة.

المسار ب: التحويل وقت الاستعلام باستخدام DAX

في السيناريو الثاني — عملة تخزين واحدة وعملة عرض يختارها المستخدم — يجب أن يحدث التحويل داخل مقياس.

النسخة الساذجة تنفّذ LOOKUPVALUE لكل صف وتصبح بطيئة بعد بضع مئات الآلاف من الصفوف. جمّع أولاً، ثم حوّل مرة واحدة:

Sales (Reporting Currency) =
VAR SelectedCurrency = SELECTEDVALUE ( Currency[Currency], "USD" )
VAR Rate =
    CALCULATE (
        SELECTEDVALUE ( FxRates[Rate] ),
        FxRates[Currency] = SelectedCurrency
    )
VAR Result =
    IF (
        SelectedCurrency = "USD",
        [Sales Amount],
        [Sales Amount] * Rate
    )
RETURN
    IF ( ISBLANK ( Rate ) && SelectedCurrency <> "USD", BLANK (), Result )

تفصيلان أهم مما يبدوان:

  • SELECTEDVALUE بقيمة افتراضية. بدون الرجوع إلى "USD" يعيد المقياس فراغاً كلما لم تُحدَّد عملة — وهي بالضبط الحالة التي يُفتح بها تقريرك.
  • الحماية الصريحة من الفراغ. إن لم يكن للعملة سعر، أعد فراغاً عن قصد بدل ترك [Sales Amount] * BLANK() يعيد صفراً. الصفر في بطاقة إيرادات كذبة؛ أما الفراغ ففجوة مرئية.

حين يتغير السعر عبر الزمن

المقياس أعلاه يستخدم سعراً حالياً واحداً لمجموعة البيانات كلها. هذا صحيح لسؤال «كم تساوي إيرادات العام الماضي اليوم» وخاطئ لكل شيء تقريباً عدا ذلك. إن كان جدول الأسعار لديك صفاً لكل عملة في كل يوم، فجمّع حسب التاريخ قبل التحويل:

Sales (Historical Rates) =
SUMX (
    VALUES ( 'Date'[Date] ),
    VAR DayRate =
        CALCULATE (
            SELECTEDVALUE ( FxRates[Rate] ),
            FxRates[Currency] = SELECTEDVALUE ( Currency[Currency], "USD" )
        )
    RETURN
        [Sales Amount] * DayRate
)

التكرار على VALUES('Date'[Date]) بدل جدول الحقائق يُبقي المكرِّر صغيراً — أياماً لا معاملات.

سلاسل التنسيق الديناميكية

رقم محوَّل مسبوق بعلامة $ ثابتة أسوأ من غياب الرمز أصلاً. في Power BI اضبط التنسيق للمقياس على ديناميكي وقدّم تعبيراً:

SELECTEDVALUE ( Currency[FormatString], "#,0.00" )

الآن تعرض البطاقة التي تظهر بالين رمز ¥ بلا خانات عشرية، دون مقياس ثانٍ. كان هذا يستلزم مجموعات الحساب في Analysis Services؛ وسلاسل التنسيق الديناميكية للمقاييس نقلته إلى Power BI نفسه.

أي سعر ينبغي أن تستخدم فعلاً؟

هذا هو السؤال الذي يفصل لوحة معلومات عن تقرير توقّع عليه الإدارة المالية، ولا توجد واجهة برمجية تجيب عنه نيابة عنك.

  • السعر الفوري بتاريخ المعاملة — لتسجيل المعاملات الفردية. أعلى دقة وأكبر جدول أسعار.
  • المتوسط الشهري أو متوسط الفترة — المعيار لبنود قائمة الدخل بموجب IAS 21 وASC 830 معاً. يُنعّم التقلّب داخل الشهر وهو ما تستخدمه معظم عمليات التوحيد فعلياً.
  • سعر إقفال الفترة — لبنود الميزانية العمومية: النقد والذمم المدينة والدائنة.
  • سعر الموازنة أو الخطة — سعر ثابت طوال السنة كي يعزل تحليل الانحرافات الأداء التشغيلي عن حركة الصرف.

النموذج الجاد يحتاج غالباً إلى اثنين أو ثلاثة منها جنباً إلى جنب، كأعمدة منفصلة في الجدول نفسه (SpotRate وAverageRate وClosingRate) لا كجداول مستقلة. وإن كان تقريرك يغذي شيئاً ينتهي في إقرار رسمي، فإن دليل أسعار الصرف والتقارير الضريبية يتناول المصدر والطابع الزمني اللذين يجب أن تستطيع الدفاع عنهما، بينما يغطي دليل الأسعار التاريخية جلب أسعار بتاريخ محدد بدل الأسعار الحية.

فجوة نهاية الأسبوع التي تكسر الإجماليات بصمت

أسواق الصرف تُغلق. جدول أسعار يومي مبني على واجهة حية لا يحتوي سبتاً ولا أحداً ولا 25 ديسمبر. اربط معاملة مؤرخة يوم سبت بذلك الجدول فتحصل على null، ويصير null فراغاً، ويصير الفراغ إجمالياً أقل بمقدار مبيعات نهاية الأسبوع بالضبط.

عالج ذلك في جدول الأسعار لا في المقياس. ولّد قائمة تواريخ كاملة واملأ للأسفل:

let
    Dates = List.Dates(#date(2026,1,1), Duration.Days(Date.From(DateTime.LocalNow()) - #date(2026,1,1)) + 1, #duration(1,0,0,0)),
    DateTable = Table.FromList(Dates, Splitter.SplitByNothing(), {"Date"}),
    Typed = Table.TransformColumnTypes(DateTable, {{"Date", type date}}),
    Joined = Table.NestedJoin(Typed, {"Date"}, RateHistory, {"Date"}, "r", JoinKind.LeftOuter),
    Expanded = Table.ExpandTableColumn(Joined, "r", {"Currency", "Rate"}),
    Filled = Table.FillDown(Expanded, {"Currency", "Rate"})
in
    Filled

تحمل Table.FillDown سعر الجمعة عبر نهاية الأسبوع، وهي المعالجة المتعارف عليها، والأهم أنها معالجة معلَنة لا عرضية. رتّب حسب العملة والتاريخ قبل الملء، وإلا حملت سعر عملة أخرى عبر الفجوة.

وإن كانت خطتك لا تشمل نقاط النهاية التاريخية، فبإمكانك بناء التاريخ إلى الأمام: أضف أسعار اليوم إلى جدول مخزّن عند كل تحديث — يصلح تدفق بيانات أو جدول Fabric Lakehouse — وبعد ربع سنة تصير لديك سلسلة زمنية حقيقية. ليست بأثر رجعي، لكنها تكلّف استدعاءً واحداً يومياً.

جدولة التحديث وحساب الحصة

يسمح Power BI Pro بـ 8 تحديثات مجدولة يومياً للنموذج الدلالي، بينما تسمح سعات Premium وFabric بـ 48. هذا هو الرقم الذي يجب أن تغطيه حصتك من الواجهة، والحساب ألطف مما يفترض معظم الناس.

جدول الأسعار أعلاه يعني استدعاءين لكل تحديث: واحد لـ /v1/currencies وآخر لـ /v1/convert. إذن:

وتيرة التحديثتحديثات شهرياًاستدعاءات شهرياًخطة Finexly
8 يومياً (حد Pro)~240~480مجانية (1000 شهرياً)
48 يومياً (Fabric، كل 30 دقيقة)~1440~2880Starter
48 يومياً + تاريخ ساعي عبر تدفق بيانات~2160~4320Growth
الخطة المجانية 1000 طلب شهرياً بسقف 10 طلبات في الدقيقة، وهو ما يغطي بارتياح مساحة عمل Pro تُحدَّث بأقصى وتيرة. الحدود الحالية وتوافر البيانات التاريخية موجودة في صفحة الأسعار.

سقف العشرة في الدقيقة هو ما يستحق الانتباه. لو بنيت استعلاماً يستدعي /v1/rate مرة لكل عملة داخل Table.AddColumn، فإن عشرين عملة تعني عشرين استدعاءً خلال ثوانٍ ودفقة من استجابات 429 في منتصف التحديث. ولهذا بالضبط وُجد الاستدعاء متعدد الأزواج /v1/convert. اجمع الطلبات وخزّن مؤقتاً: دليل التخزين المؤقت ومعالجة الأخطاء يغطي أنماط إعادة المحاولة بتراجع تدريجي، وهي تنطبق على التحديث المجدول بالقدر نفسه.

البوابات وExcel وFabric

ملاحظات بيئية توفّر كل واحدة منها بعد ظهيرة كامل:

  • لا حاجة إلى بوابة. واجهة REST سحابية ليست مصدراً محلياً، فلا تحتاج إلى بوابة بيانات محلية لهذا. وإن فشل التحديث واقترح أحدهم تثبيت بوابة، فذلك غالباً خطأ مصدر البيانات الديناميكي متنكّراً.
  • Excel يستخدم المحرك نفسه. يقبل Power Query في Excel شيفرة M أعلاه حرفياً. وإن كان جمهورك يعيش في المصنّفات لا في لوحات المعلومات، فإن دليل الأسعار الحية في Excel يغطي WEBSERVICE وLAMBDA ومصفوفة الإصدارات، وهناك نظير لجداول بيانات Google.
  • Fabric Dataflow Gen2 مكان أفضل لجدول الأسعار حالما يحتاجه أكثر من تقرير. أنزل الأسعار مرة واحدة ودع كل النماذج الدلالية تقرأ الجدول نفسه، فيتوقف استهلاك الواجهة عن التوسع مع عدد التقارير.
  • قارن برقم معروف قبل النشر. خذ زوجاً من محوّل العملات وقارنه بما يعرضه نموذجك للزوج نفسه في اللحظة نفسها. إن اختلفا فلديك مشكلة اتجاه أو تقريب، ويحسن أن تجدها الآن لا في اجتماع مجلس الإدارة.

أسئلة شائعة

هل يستطيع Power BI تحويل العملات دون واجهة برمجية؟ نعم، إن وفّرت الأسعار بنفسك: جدول تحدّثه يدوياً، أو تصدير من النظام المالي، أو عرض في قاعدة بيانات. لا يوجد في Power BI مصدر أسعار مدمج. تصبح الواجهة مهمة حين تحتاج أسعاراً تتحدث دون أن يتذكر أحد تحديثها.

لماذا يُحدَّث تقرير التحويل في Power BI Desktop ويفشل في الخدمة؟ غالباً بسبب خطأ مصدر البيانات الديناميكي. استدعاء Web.Contents لديك يبني العنوان بدمج النصوص. انقل الأجزاء المتغيرة إلى خياري RelativePath وQuery ليبقى العنوان الأساسي ثابتاً، ثم أعد النشر وأدخل بيانات الاعتماد من جديد.

هل أحوّل العملات في Power Query أم في DAX؟ في Power Query حين يكون للتقرير عملة تقارير واحدة — أسرع وأبسط. وفي DAX حين يختار المستخدم العملة أثناء التشغيل. وإن احتجت الاثنين، وحّد إلى عملة محورية في Power Query ثم ضع مقياس DAX فوقها.

كم طلباً تستهلك عملية تحديث واحدة في Power BI؟ طلبان لكل تحديث إن جمعت كل الأزواج في استدعاء /v1/convert واحد. وعند حد Power BI Pro البالغ 8 تحديثات يومياً يكون ذلك نحو 480 طلباً شهرياً، ضمن الخطة المجانية. ولا يصبح مكلفاً إلا إذا استدعيت الواجهة لكل عملة أو لكل صف.

كيف أتعامل مع عطلات نهاية الأسبوع والعطل الرسمية في جدول أسعار يومي؟ ولّد جدول تواريخ متصلاً، واربط الأسعار به ربطاً أيسر، ورتّب حسب العملة والتاريخ، ثم املأ للأسفل. ينتقل سعر الجمعة عبر نهاية الأسبوع. المهم أن تكون المعالجة مقصودة وموثّقة، لا أن تختفي الصفوف بصمت.

أي سعر صرف أستخدم في التقارير المالية؟ متوسطات الفترة لبنود قائمة الدخل، وأسعار الإقفال لبنود الميزانية العمومية، بموجب IAS 21 وASC 830 معاً. خزّنها كأعمدة منفصلة في جدول أسعار واحد كي يبدّل التقرير بينها دون تغيير النموذج.


جاهز لوضع أسعار حية خلف لوحات معلوماتك؟ احصل على مفتاح Finexly المجاني — دون بطاقة ائتمان. ابدأ بـ 1000 طلب شهرياً، وهو ما يكفي لتحديث مساحة عمل Power BI Pro بأقصى وتيرة، وارتقِ بالخطة حين تحتاج بيانات تاريخية أو وتيرة أسرع. وإن كنت لا تزال تقارن المزوّدين، فإن صفحة المقارنة تضع الخيارات جنباً إلى جنب.

Vlado Grigirov

Senior Currency Markets Analyst & Financial Strategist

Vlado Grigirov is a senior currency markets analyst and financial strategist with over 14 years of experience in foreign exchange markets, cross-border finance, and currency risk management. He has wo...

View full profile →