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

كيفية الحصول على أسعار الصرف الحية في Power BI: Power Query وDAX وفخ التحديث

V
Vlado Grigirov
September 01, 2026
Currency API Exchange Rates Power BI Power Query DAX Tutorial Finexly

الحصول على أسعار الصرف الحية في Power BI واحدة من تلك المهام التي تبدو منتهية قبل أن تنتهي فعلاً بوقت طويل. تلصق رابط واجهة برمجية في الحصول على البيانات ← ويب، فيظهر جدول أسعار، ويضيء مقياس الإيرادات المحوَّلة، فتنشر التقرير. بعد يومين يفشل تحديث مجموعة البيانات في خدمة Power BI برسالة عن مصدر بيانات ديناميكي — أو ينجح التحديث لكنه يحوّل بصمت كل معاملة تاريخية بسعر اليوم.

يغطي هذا الدليل المسار كاملاً: استعلام M في Power Query يعيد جدول أسعار نظيفًا، ونمط RelativePath الذي يبقيه قابلاً للتحديث في الخدمة، ودالة أسعار تاريخية للتحويل بتاريخ المعاملة، ومقياس DAX لعملة تقارير ديناميكية، وحساب عدد الطلبات الذي يحدد ما إذا كان جدول التحديث لديك يتسع داخل خطة الواجهة البرمجية.

كل الاستعلامات أدناه مكتوبة وفق أشكال الاستجابة الموثقة في واجهة Finexly البرمجية. إن كنت قرأت دليلنا عن أسعار الصرف الحية في Excel فستبدو لغة M هنا مألوفة — لكن Power BI يضيف طبقة تحديث على جانب الخدمة لا وجود لها في Excel، وعند تلك الطبقة تحديدًا تنهار معظم هذه المشاريع.

الطرق الثلاث لإدخال الأسعار إلى Power BI

الطريقةتُحدَّث في الخدمة؟مفتاح الواجهة آمن؟أسعار تاريخيةالأنسب لـ
موصّل الويب، ولصق الرابط في مربع الحوارغالبًا لا — يتحول الرابط عادةً إلى مصدر بيانات ديناميكي❌ المفتاح ظاهر في الرابطلاإثبات مفهوم في خمس دقائق
استعلام فارغ مع Web.Contents + RelativePath✅ نعم✅ عبر الترويسة✅ نعمكل نموذج حقيقي تقريبًا
تدفق بيانات (أو مسار Fabric) يغذّي جدول الأسعار✅ نعم، ومنفصل عن تحديث التقرير✅ عبر الترويسة✅ نعمتقارير متعددة وأعداد صفوف كبيرة
الصف الثاني هو الجواب الافتراضي. والثالث هو ما تنتقل إليه حين يحتاج أكثر من تقرير إلى الأسعار نفسها. أما الأول فهو ما تعلّمه كل الشروحات القائمة على لقطات الشاشة، وهو سبب فشل هذا الكم من نماذج العملات عند أول تحديث مجدول.

الطريقة الأولى: جدول أسعار حي باستخدام Power Query

افتح تحويل البيانات ← مصدر جديد ← استعلام فارغ ← المحرر المتقدم والصق ما يلي. يعيد صفًا لكل زوج عملات مع طابع زمني بتوقيت UTC لوقت الجلب.

let
    ApiKey  = "YOUR_API_KEY",
    Base    = "USD",
    Symbols = "EUR,GBP,JPY,CHF,AUD,CAD,SEK,NZD",

    Source = Json.Document(
        Web.Contents(
            "https://api.finexly.com",
            [
                RelativePath = "v1/latest",
                Query        = [ base = Base, symbols = Symbols ],
                Headers      = [ #"Authorization" = "Bearer " & ApiKey ]
            ]
        )
    ),

    Rates    = Record.ToTable( Source[rates] ),
    Renamed  = Table.RenameColumns( Rates, {{"Name", "Quote"}, {"Value", "Rate"}} ),
    AddBase  = Table.AddColumn( Renamed, "Base", each Base, type text ),
    AddStamp = Table.AddColumn( AddBase, "RetrievedUTC", each DateTimeZone.UtcNow(), type datetimezone ),
    Typed    = Table.TransformColumnTypes(
                   AddStamp,
                   {{"Quote", type text}, {"Rate", type number}}
               )
in
    Typed

نقطة النهاية التي يستدعيها تبدو هكذا في سطر الأوامر، ويستحق الأمر تشغيلها مرة واحدة لترى البنية التي تحلّلها:

curl "https://api.finexly.com/v1/latest?base=USD&symbols=EUR,GBP,JPY" \
  -H "Authorization: Bearer YOUR_API_KEY"
{
  "success": true,
  "base": "USD",
  "date": "2026-09-01",
  "rates": { "EUR": "…", "GBP": "…", "JPY": "…" }
}

لماذا ليست RelativePath و Query اختيارية

هذه أهم نقطة في المقال، ولذلك أفردنا لها عنوانًا خاصًا.

إذا بنيت الرابط سلسلة نصية واحدة مدمجة — "https://api.finexly.com/v1/latest?base=" & Base — فلن يتمكن Power Query من تحديد الوجهة قبل تنفيذ الاستعلام فعليًا. تصنّف Microsoft ذلك بوصفه مصدر بيانات ديناميكي، والمصادر الديناميكية لا تُحدَّث في خدمة Power BI لأسباب تتعلق بالأمان والخصوصية. سيُحدَّث التقرير على حاسوبك المحمول بلا مشكلة، ويفشل في اللحظة التي يُجدوَل فيها.

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

  1. يجب أن يكون الرابط الأساسي سلسلة حرفية. بلا معاملات، وبلا دمج، وبلا أي & فيه.
  2. RelativePath هو مسار نقطة النهاية الثابت. أي "v1/latest"، لا "v1/latest?base=USD".
  3. لا تدمج النصوص داخل Query أبدًا. مرّر سجلاً من أزواج الاسم/القيمة ودع Power Query يتولى ترميز الرابط. سيتولى أيضًا تهريب المحارف التي كانت ستكسر الطلب.

ضبط بيانات الاعتماد

عند تشغيل الاستعلام أول مرة يسأل Power BI عن طريقة المصادقة مع https://api.finexly.com. اختر مجهول (Anonymous). يبدو ذلك خاطئًا لكنه الصواب: مفتاح الواجهة يسافر في ترويسة Authorization التي وضعتها في كود M، لا عبر مخزن بيانات الاعتماد في Power BI. اختيار Web API أو أساسي سيجعل Power BI يضيف ترويسته الخاصة وسيُرفض الطلب.

اضبط مستوى الخصوصية على عام أو مؤسسي بشكل متسق عبر كل مصادرك. عدم تطابق مستويات الخصوصية هو ثاني أكثر أسباب التحديث الذي ينجح في Desktop ويفشل في الخدمة — إذ يفضّل Power BI حجب الاستعلام على المخاطرة بتسريب بيانات مصدر داخل طلب مصدر آخر.

ملاحظة صريحة: المفتاح مخزّن الآن كنص صريح داخل الاستعلام. أي شخص يفتح ملف .pbix يستطيع قراءته. لأي شيء يُشارَك خارج جهازك، ارفع المفتاح إلى معامل في Power Query واحتفظ بالنسخة المعبّأة داخل تدفق بيانات يملكه حساب خدمة، فيستهلك مؤلفو التقارير جدول الأسعار دون أن يروا بيانات الاعتماد إطلاقًا.

الطريقة الثانية: أسعار تاريخية للتحويل بتاريخ المعاملة

جدول الأسعار الحي يجيب عن سؤال «كم يساوي EUR/USD الآن». وهو لا يجيب عن «كم كانت إيرادات يناير بالدولار»، واستخدامه لذلك أغلى خطأ في هذا الموضوع كله — فإعادة عرض أرقام الربع الماضي لأن النموذج أعاد تحويلها بسعر جديد هو بالضبط ما يبحث عنه المدققون. إن كان تقريرك يغذي شيئًا ستضطر للدفاع عنه لاحقًا، فاقرأ إلى جانب هذا القسم دليلنا عن أسعار الصرف والإقرار الضريبي.

تحتاج إلى جدول أسعار مفهرس بالتاريخ. تغلّف دالة M التالية نقطة نهاية السلاسل الزمنية وتعيد صفًا لكل تاريخ ولكل عملة:

let
    FxHistory = (base as text, symbols as text, startDate as date, endDate as date) as table =>
        let
            ApiKey = "YOUR_API_KEY",

            Source = Json.Document(
                Web.Contents(
                    "https://api.finexly.com",
                    [
                        RelativePath = "v1/timeseries",
                        Query = [
                            base       = base,
                            symbols    = symbols,
                            start_date = Date.ToText( startDate, [Format = "yyyy-MM-dd", Culture = "en-US"] ),
                            end_date   = Date.ToText( endDate,   [Format = "yyyy-MM-dd", Culture = "en-US"] )
                        ],
                        Headers = [ #"Authorization" = "Bearer " & ApiKey ]
                    ]
                )
            ),

            Days      = Table.RenameColumns( Record.ToTable( Source[rates] ), {{"Name", "RateDate"}} ),
            Expanded  = Table.ExpandRecordColumn( Days, "Value", Record.FieldNames( Days{0}[Value] ) ),
            Unpivoted = Table.UnpivotOtherColumns( Expanded, {"RateDate"}, "Quote", "Rate" ),
            AsDate    = Table.TransformColumns(
                            Unpivoted,
                            {{"RateDate", each Date.FromText( _, [Format = "yyyy-MM-dd", Culture = "en-US"] ), type date}}
                        ),
            AddBase   = Table.AddColumn( AsDate, "Base", each base, type text ),
            Typed     = Table.TransformColumnTypes( AddBase, {{"Quote", type text}, {"Rate", type number}} )
        in
            Typed
in
    FxHistory

تفصيلتان هنا تؤديان عملاً حقيقيًا:

  • Culture = "en-US" في Date.ToText وDate.FromText معًا. بدونها، يرسل جهاز مضبوط على لغة ألمانية أو فرنسية القيمة 01.09.2026 بوصفها start_date فترفضها الواجهة — أو أسوأ، ينتج تحديث زميلك جدولاً بشكل مختلف عن جدولك. الإعدادات المحلية هي المتغير غير المرئي في كل مشروع Power Query يمتد عبر أكثر من بلد.
  • Table.UnpivotOtherColumns. تعيد الواجهة التواريخ كمفاتيح سجل مع سجل متداخل للعملات. وإلغاء المحورية إلى الشكل الطويل RateDate / Base / Quote / Rate يمنحك جدولاً يرتبط بنظافة ببُعد التاريخ ولا يحتاج إعادة تشكيل كلما أضفت عملة.

استدعِها مرة واحدة لكل تحميل: FxHistory( "USD", "EUR,GBP,JPY", #date(2026,1,1), Date.From( DateTime.LocalNow() ) ).

ولأن أسعار التواريخ الماضية لا تتغير أبدًا، فهذا الجدول هو الحالة النموذجية للتحديث التزايدي: قسّم على RateDate، وحدّث آخر 7 أيام، وأرشف ما قبلها. عندئذ يتوقف زمن التحديث عن النمو مع تاريخك، ويتوقف عدد الطلبات عن النمو معه أيضًا.

لا تستدعِ الواجهة مرة لكل صف

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

احسب قبل أن تبني. يسمح Power BI Pro بـ 8 تحديثات مجدولة لكل مجموعة بيانات يوميًا؛ ويسمح Premium Per User وسعة Fabric بـ 48. استدعاء واحد لـ /v1/latest يغطي كل العملات التي تحتاجها، بأقصى معدل في Pro، يكلّف 8 طلبات يوميًا — نحو 240 شهريًا، وهو داخل حد مجاني قدره 1000 بفارق مريح. الجدول نفسه مع دالة لكل صف بلا سقف. وحتى عند حد PPU البالغ 48 تحديثًا، يستقر الاستدعاء الموحّد عند نحو 1440 طلبًا شهريًا، وهي خطة مدفوعة صغيرة لا كارثة صف بصف. تسرد خطط الأسعار لدينا العتبات إن أردت تقديرًا دقيقًا.

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

تحويل المبالغ: دمج في Power Query أم مقياس DAX

هناك موضعان مشروعان لتطبيق التحويل، وكلٌّ منهما يجيب عن سؤال مختلف.

الخيار أ: الدمج في Power Query

إن كانت عملة التقارير ثابتة — كل شيء يُعرض بالدولار، وانتهى — فنفّذ الربط وقت التحميل.

  1. حمّل جدول الحقائق في Power Query.
  2. دمج الاستعلامات مع جدول الأسعار، بالمطابقة على رمز العملة و التاريخ. أمسك Ctrl واختر الأعمدة بالترتيب نفسه في الجدولين.
  3. وسّع عمود Rate فقط واضبطه على رقم عشري ثابت.
  4. حدّد Amount وRate ثم إضافة عمود ← قياسي ← ضرب.
  5. عطّل تحميل جدول الأسعار إن لم يشر إليه شيء آخر.

هذا سريع، ويتجسّد مرة واحدة، ولا يمكن لمقسّم شرائح أن يعبث به. والنقطة الأخيرة هي المقايضة.

الخيار ب: مقياس DAX بعملة تقارير قابلة للاختيار

إن احتاج المستخدمون إلى تبديل التقرير كله بين USD وEUR وGBP، فيجب أن يحدث التحويل وقت الاستعلام. أضف جدولاً غير مرتبط باسم Reporting Currency فيه عمود واحد لرموز ISO، وضعه في مقسّم شرائح، ثم اكتب:

Revenue (Reporting Currency) =
VAR ReportingCurrency = SELECTEDVALUE( 'Reporting Currency'[Code], "USD" )
RETURN
SUMX (
    'Sales',
    VAR TxCurrency = 'Sales'[CurrencyCode]
    VAR TxDate     = 'Sales'[OrderDate]
    VAR Rate =
        CALCULATE (
            MAX ( 'FX Rates'[Rate] ),
            REMOVEFILTERS ( 'FX Rates' ),
            'FX Rates'[Base]     = TxCurrency,
            'FX Rates'[Quote]    = ReportingCurrency,
            'FX Rates'[RateDate] = TxDate
        )
    RETURN 'Sales'[Amount] * Rate
)

ثلاثة أمور جديرة بالانتباه:

  • سطور VAR حاملة للبنية. التقاط TxCurrency وTxDate في متغيرات قبل CALCULATE يثبّتهما على صف SUMX الحالي. أما الإشارة إلى الأعمدة مباشرة داخل وسائط التصفية فتستدعي خطأ انتقال السياق الذي ينتج مجاميع تبدو معقولة لكنها خاطئة.
  • REMOVEFILTERS( 'FX Rates' ) يمنع أي مرشّح وارد على جدول الأسعار من تضييق البحث.
  • غياب السعر يعيد BLANK()، وAmount * BLANK() تساوي BLANK(). هذا هو السلوك المطلوب. فالصف الذي يرتد بصمت إلى المبلغ غير المحوَّل هو صف ينفخ مجموعك بمقدار سعر الصرف.

فخ العلاقات

لا تنشئ علاقة فعلية من جدول الحقائق إلى جدول الأسعار على رمز العملة وحده. فرمز العملة ليس فريدًا في جدول أسعار مفهرس بالتاريخ، لذا يقترح Power BI علاقة متعدد إلى متعدد، ومتعدد إلى متعدد مع تصفية ثنائية الاتجاه سيضاعف صفوفك وإيراداتك بلا تردد. إما أن تستخدم الدمج المركّب في Power Query، أو تُبقي جدول الأسعار غير مرتبط وتبحث عن الأسعار في DAX كما سبق.

خمسة أخطاء تنتج أرقامًا خاطئة دون رسالة خطأ

  1. الروابط المدمجة نصيًا. تُحدَّث في Desktop، وتفشل في الخدمة برسالة «تتضمن مجموعة البيانات هذه مصدر بيانات ديناميكيًا». والعلاج RelativePath وQuery.
  2. أزواج مقلوبة. base=USD&symbols=EUR يعيد USD→EUR. وإن كان جدول حقائقك يخزّن مبالغ باليورو فأنت تحتاج المقلوب. تحقق يدويًا من زوج معروف قبل أن تثق بأي مجموع. ويفيدك مرجع ISO 4217 لدينا إن التبس عليك أي رمز يقع في أي جانب.
  3. سعر اليوم على صفوف تاريخية. الرقم يتغير مع كل تحديث ولا ينتبه أحد حتى يقارن شخص ما تصديرين للتقرير نفسه.
  4. تواريخ وأعداد عشرية بتنسيق محلي. الفاصلة العشرية تحوّل 1,0842 إلى نص، فتعيد Table.TransformColumnTypes خطأً يعامله النموذج كقيمة فارغة، فتختفي الصفوف المتأثرة من مجاميعك.
  5. التقريب في الخطوة الخطأ. قرّب مرة واحدة، عند العرض، بعد الضرب. تقريب السعر إلى أربع منازل قبل ضربه في مبلغ من سبعة أرقام يُحدث فارقًا مرئيًا أمام النظام المحاسبي.

الأسئلة الشائعة

هل يستطيع Power BI تحديث أسعار الصرف تلقائيًا؟

نعم. يمكن جدولة مجموعة بيانات منشورة لما يصل إلى 8 تحديثات يوميًا على Power BI Pro و48 يوميًا على Premium Per User أو سعة Fabric. وتتيح نقطة نهاية XMLA في Premium للأدوات الخارجية تشغيل تحديثات خارج هذه الحدود. أما استعلام الأسعار نفسه فلا يحتاج معالجة خاصة سوى أن يُبنى بـ Web.Contents + RelativePath كي تقبله الخدمة.

كيف أمرّر مفتاح الواجهة البرمجية بأمان في Power BI؟

ضعه في سجل Headers داخل Web.Contents بالصيغة Authorization = "Bearer " & ApiKey واختر المصادقة مجهول حين يسأل Power BI. هذا يُبقي المفتاح خارج الرابط، وخارج سجلات الوكيل، وخارج سجل المتصفح. لكنه لا يشفّر المفتاح داخل ملف .pbix — فللنماذج المشتركة، احتفظ بالمفتاح في تدفق بيانات يملكه حساب خدمة ودع التقارير تقرأ الجدول المجسَّد.

لماذا تقول مجموعة البيانات «تتضمن مجموعة البيانات هذه مصدر بيانات ديناميكيًا»؟

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

كيف أحوّل المبالغ بسعر الصرف في تاريخ المعاملة؟

حمّل جدول أسعار مفهرسًا بالتاريخ من نقطة نهاية السلاسل الزمنية، ثم طابق على العملة و التاريخ معًا — إما عبر دمج مركّب في Power Query، أو ببحث CALCULATE داخل SUMX كما تقدّم. لا تربط على العملة وحدها أبدًا؛ فستلتقط بصمت صف السعر الذي يصادف أنه الأول في الترتيب.

هل توجد واجهة عملات مجانية تعمل مع Power BI؟

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


ابدأ مع Finexly

مستعد لإدخال أسعار الصرف الحية إلى تقارير Power BI لديك؟ احصل على مفتاح Finexly المجاني — دون بطاقة ائتمان. ابدأ بألف طلب مجاني شهريًا وارتقِ بالخطة مع نموك. أسعار لحظية وتاريخية لأكثر من 170 عملة، من واجهة REST واحدة تتصرف بالطريقة نفسها سواء استدعيتها من Power Query أو Python أو خدمة مدفوعات.

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 →