الرئيسية سجل المعايرة إخلاء المسؤولية التوثيق English
Metric Risk · QuantMarketEngine

تشغيل المنصة

ملف الـ README بيغطّي المحرّك وتقاريره. وهاي الصفحة بتغطّي الباقي — لوحة المشتركين، الـ API العام، قاعدة البيانات اللي بيتشاركوها، وشو بدّه الأمر عشان تحطّ التلاتة على خادم.

كل إشي تحت انشتغل واتفحّص على هاد المستودع، مش مكتوب من الذاكرة.

$6–8 بالشهر لتشغيل المنصة كلها، وأغلبها خادم افتراضي صغير

المستضيفات الأربعة

البوت بيشتغل لحاله. واللوحة والـ API هم اللي بيلمسهم العميل.

TelegramBot console

المحرّك. بيولّد التقارير، بيقيّم القراءات السابقة، بينشر على تيليجرام وعلى الموقع الثابت. وهو كمان مُشغّل الأوامر المفردة.

Dashboard port 5199

تطبيق Blazor: صفحة الهبوط، الأسعار، التوثيق، التسجيل، ومنطقة المشترك — التقارير، مفاتيح الـ API، التنبيهات، الفوترة.

API port 5006

واجهة REST العامة اللي بيناديها المشتركون بـ X-Api-Key.

Migrator console

بيطبّق الترحيلات وبيزرع كتالوج الخطط، وبعدين بيخرج. للنشرات اللي بتشغّل أكتر من عقدة.

المتطلّبات

‏.NET 10 SDK. وبس — قاعدة البيانات SQLite، بتنعمل عند أول تشغيل.

واتنين بيانات اعتماد إنت اللي بتوفّرهم، وكلاهما فاضي في appsettings.json عن قصد:

AlphaVantage:ApiKey

مزوّد بيانات السوق. وبدون مفتاح، المحرّك بيرجع لمزوّد الـ CSV المرفق، فبيشتغل وبينقاس بلا إنترنت.

Telegram:BotToken

بس إذا بدّك قناة تيليجرام. وكل إشي تاني بيشتغل بدونه.

خلّيهم برّا المستودع. في التطوير:

# from QuantMarketEngine/src/QuantMarketEngine.TelegramBot
dotnet user-secrets set "AlphaVantage:ApiKey" "YOUR_KEY"
dotnet user-secrets set "Telegram:BotToken"   "YOUR_TOKEN"

وفي الإنتاج، متغيّرات بيئة: AlphaVantage__ApiKey و Telegram__BotToken.

شو لازم تسجّل فيه، وقدّيش بيكلّف

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

بيانات السوق — مطلوبة للأسعار الحيّة

المزوّد

AlphaVantage افتراضياً. تسجيل مجاني، بلا بطاقة.

التكلفة الفعلية

4 نداءات لكل رمز لكل تقرير. مقيسة مش مقدَّرة — البوت بيطبع عدّاد نداءاته آخر كل تشغيلة.

الحصة المجانية

25 نداء باليوم، وهي اللي بيقيس عليها عدّاد المحرّك.

شو بيسع

تلات رموز باليوم ≈ 12 نداء — مرتاحة جوّاها. والمستودع بيتشحن مضبوطاً على تلاتة: يورو/دولار، ذهب، فضة.

إمتى بتدفع

رموز أكتر، أو أطر زمنية داخل اليوم. وباقاتهم المدفوعة بتبلّش حوالي 50 دولار بالشهر.

تأكّد من شروطهم الحالية قبل ما تعتمد على هاد

رقم الـ 25 باليوم هو اللي بيقيس عليه المحرّك، وكان صحيحاً وقت الكتابة. وتسعير مزوّد خارجي قراره إلهم مش إلنا. وإنت مش مربوط فيهم أصلاً — تبديل المزوّد شغلة صغيرة، موصوفة تحت.

تيليجرام — اختياري

بس إذا بدّك قناة النشر. التوكن مجاني من @BotFather جوّا تيليجرام نفسه، بدقيقة تقريباً. وبدونه المحرّك بيشتغل طبيعي تماماً — بيكتب تقاريره على القرص وعلى الموقع الثابت، وبس ما بينشر على تيليجرام.

التشغيل بلا أي حساب إطلاقاً

CsvFileProvider بيجي مع المستودع ومعه الشموع التاريخية، يعني المحرّك بيشتغل وبيطلّع تقارير وبينقاس من الطرف للطرف بلا تسجيل بأي مكان. وهيك بالضبط انعملت كل دراسات المعايرة بهاد المشروع — وكل وحدة فيهم بتقول «صفر نداءات للمزوّد». استخدمه عشان تقيّم المحرّك قبل ما تسجّل بأي إشي.

الفاتورة الشهرية كاملة

بيانات السوق — الباقة المجانية بتلات رموز$0
تيليجرام$0
خادم افتراضي صغير$5–7
نطاق — حوالي 12–15 دولار بالسنة~$1
شهادة TLS — من Let's Encrypt$0
قاعدة البيانات — SQLite على نفس الخادم$0
المجموع$6–8 / شهر

و Stripe ما إله رسم شهري — بياخد نسبة على كل معاملة. والبريد الصادر إله باقات مجانية بمئات الرسائل يومياً، وهاد أكتر بكتير من حاجة استعادة كلمات المرور.

اختيار الأدوات اللي بتغطّيها

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

وين بتعيش القائمة

في قاعدة البيانات، بتنُدار من واجهة الويب/admin/symbols باللوحة. ضيف أداة، وقّف وحدة، رجّع وحدة؛ والتغيير بيسري بالتشغيلة المجدولة الجاي، بلا إعادة تشغيل وبلا ملف تعدّله.

ومين بيقدر يوصل هديك الصفحة محدَّد بـ Admin:Emails في إعدادات اللوحة. خلّيه فاضي وما حدا بيوصل، وإنت منهم. وهاد مقصود: القائمة الفاضية لو انقرأت «بلا قيود» كانت رح تفتح الصفحة لكل مشترك على نشرة نسيت الإعداد ببساطة.

وTelegram:ActiveSymbols صار بذرة وبس. عند أول تشغيلة على قاعدة فاضية بيصير محتواه هو الصفوف الأولى؛ وبعدها الجدول هو المرجع والإعداد ما بينسأل عنه تاني — يعني إطفاء أداة من المتصفّح ما بيرجّعه تشغيل الخادم الجاي.

والتشغيلة المفردة بتقدر تستهدف أداة وحدة بغضّ النظر عن القائمة:

dotnet run -- report GBPUSD 1D

وتذكّر التكلفة: 4 نداءات لكل رمز لكل تقرير. على حصة 25 باليوم، تلات رموز مريحة وستّة هي السقف.

إضافة أداة بالطريقة الصح

تأكّد إن مزوّدك بيحملها، وبأي رمز

رمز المزوّد ممكن ما يكون GBPUSD. ترجم جوّا المزوّد؛ والمحرّك بيحتفظ بتسمياته هو.

ضيفها من /admin/symbols

الرمز، واسم العرض اختيارياً. وإعادة إضافة أداة موقوفة بترجّعها — بشموعها وتقاريرها وسجل معايرتها.

ازرع تاريخها، مرة وحدة

اضغط Fetch history على صفّها — البوت بياخد الطلب خلال ثواني والنتيجة بتظهر تحت Recent work، ومعها عدد النداءات اللي صرفها.

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

dotnet run -- seedhistory   # full daily history, every active symbol
dotnet run -- backfill      # walk-forward scoring — fills the calibration cards

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

أعطيها عُرف تسعير إذا بتحتاج

PriceFormatter بيقرّر عدد الخانات العشرية من الرمز: أي إشي فيه JPY ← 3، وXAU/GOLD ← 2، و XAG/SILVER ← 3، وكل إشي غيرهم ← 5. وهاد الافتراضي صح لعملات الفوركس الرئيسية وغلط لمؤشر أو زوج عملة رقمية. وهو صنف صغير بدالة وحدة، وإضافة قاعدة تغيير بسطرين.

زر «Report now» ما بينشر

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

وهاي الثغرة مسجّلة

الذهب طُبع مرة بخمس خانات عشرية — 4021.26444 — لأن المنسّق كان بيعرف الـ JPY وبس. والسعر اللي المكتب ما بيقدر يقراه بيكلّف مصداقية أكتر مما بيبيّن.

شو اللي ما بيكون عند أداة جديدة في يومها الأول

سجلّ معايرة. والمحرّك ما بيدّعي سجلاً ما اكتسبه — صفحة المعايرة بتطبع «العيّنة صغيرة» لحد ما يصير في 20 نتيجة مقيَّمة لهاد الرمز، ورقم الثقة بيرجع لقراءة مطلقة بدل رتبة داخل تاريخ لسّه ما عنده إياه. وbackfill بيقصّر هاي المدة بتقييم الماضي بتمريرة وحدة؛ وبعدها السجل بينراكم يومياً.

حدّ واحد حقيقي — وهو عن البيانات، مش عن المحرّك

الذهب والفضة شغّالين على تغذية بتعطي سعراً واحداً باليوم بلا مدى داخل اليوم. والنتيجة بنيوية وبتستاهل تفهمها قبل ما تضيف أدوات مشابهة:

  • جودة السوق بتستقر على FAIR وبتضل هناك.
  • عدة قراءات بتنحجب عن قصد بدل ما تنقدَّر من مدى مش موجود.
  • معايرة الفضة لسّه بتقرأ «العيّنة صغيرة»، لأن النتائج اللي بتقدر تقيّمها أقل.

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

فلما تضيف أداة، اسأل شو بتعطيك تغذيتها فعلاً. الشمعة اليومية الكاملة بتعطيك كل اللي عند اليورو/دولار. والسعر الإغلاقي الوحيد بيعطيك تقريراً شغّالاً بس أهدأ — والمحرّك رح يقول للقارئ أي وحدة منهم بدل ما يغطّي عليها.

استخدام مزوّد بيانات مختلف

ما في إشي بيربط هاد المحرّك بـ AlphaVantage. طبقات التحليل ما بتشوف مزوّداً أبداً — بتطلب شموعاً عبر واجهة، والمزوّد بيقعد وراها. وفي تطبيقين موجودين أصلاً، فالنمط مش نظرياً: AlphaVantageProvider عبر HTTP و CsvFileProvider من ملفات على القرص.

شو لازم يوفّره المزوّد

واجهتين، في Domain/Interfaces، صغيرتين عن قصد:

public interface IMarketDataProvider
{
    Task<IReadOnlyList<MarketSnapshot>> FetchAsync(
        string symbol, string timeframe, CancellationToken ct = default);
}

public interface IHistoricalMarketDataProvider
{
    Task<IReadOnlyList<MarketSnapshot>> FetchHistoryAsync(
        string symbol, string timeframe, CancellationToken ct = default);
}

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

وMarketSnapshot هو اللي بتتوقّعه — ما فيه إشي بشكل مزوّد معيّن:

Symbol · Timeframe · Timestamp

أي شمعة هاي.

Open · High · Low · Close

الفتح والأعلى والأدنى والإغلاق.

Volume

بيقبل الفراغ — تغذيات الفوركس غالباً بلا حجم، والمحرّك ما بيشترطه.

DataQualityScore

تقييمك إنت للشمعة من 0 لـ 100.

التلات خطوات

اكتب الصنف

حطّه في Infrastructure/DataProviders جنب التنين التانيين، ورجّع اللقطات مرتّبة من الأقدم للأحدث.

سجّله

في ServiceCollectionExtensions.cs، بنفس المكان اللي انسجّل فيه المزوّدان الموجودان، أشّر الواجهتين على صنفك:

services.AddScoped<IMarketDataProvider, MyVendorProvider>();
services.AddScoped<IHistoricalMarketDataProvider, MyVendorProvider>();

اربط الرموز

رمز مزوّدك لليورو/دولار ممكن ما يكون EURUSD. ترجم جوّا مزوّدك — وباقي المحرّك بيضل يستخدم تسمياته هو.

وهاد كل التغيير. ولا طبقة تحليل، ولا مُصيِّر، ولا تقرير، ولا اختبار بيلمس اسم مزوّد.

فحص السعر ما قبل النشر مش مزوّدك عن قصد

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

في معالج تحديد معدّل مربوط بـ AlphaVantage تحديداً

إذا مزوّدك إله حدّ مختلف، اكتب ما يقابله — أو احذفه إذا ما عندهم حدّ. والمحرّك بيعدّ نداءاته بكل الأحوال، وبيطبع المجموع آخر كل تشغيلة.

إذا عندك بياناتك أنت

CsvFileProvider بيقرأ OHLC من ملفات على القرص أصلاً. فإذا عندك تاريخ بصيغة CSV — من تصدير وسيط، أو من منصة، أو من مزوّد بتدفعله أصلاً — يمكن ما بتحتاج صنفاً جديداً أبداً: أشّر مجلد بيانات الـ CSV على ملفاتك والمحرّك بيشتغل عليها. وهيك بالضبط انعملت كل دراسة معايرة بهاد المشروع.

الإشي الوحيد اللي بيوقّع الناس

المستضيفات التلاتة لازم تأشّر على نفس ملف SQLite

وهم ما بيعملوا هيك افتراضياً. كل واحد فيهم رح يعمل quant.db خاص فيه جنبه، وبتطلع معك لوحة التسجيل فيها بيشتغل، وما بتظهر ولا تقرير أبداً، وما في إشي بالسجلات بيبيّن غلط.

وهاد المستودع رابطهم أصلاً بمسار نسبي:

TelegramBot

Data Source=quant.db

API

Data Source=../QuantMarketEngine.TelegramBot/quant.db

Dashboard

Data Source=../QuantMarketEngine.TelegramBot/quant.db

وهاد بيمشي لما التلاتة يشتغلوا من مجلداتهم على جهاز واحد، وهاد هو ترتيب التطوير. وعلى الخادم، حطّ التلاتة على مسار مطلق واحد — وحدة تخزين بتتشاركها الحاويات، أو مسار على الجهاز:

ConnectionStrings__Quant=Data Source=/data/quant.db

وإذا انتقلت لـ PostgreSQL، مزوّد Npgsql مرجَّع أصلاً.

التشغيل محلياً

تلات طرفيات، أو شغّل اللي بتحتاجه بس.

المحرّك — بيولّد تقرير اليوم لكل رمز نشط:

# from QuantMarketEngine/src/QuantMarketEngine.TelegramBot
dotnet run -- report

اللوحة — الموقع اللي بيسجّل فيه المشترك:

dotnet run --project QuantMarketEngine/src/QuantMarketEngine.Dashboard --urls http://localhost:5199

الـ API — اللي بيناديه X-Api-Key:

dotnet run --project QuantMarketEngine/src/QuantMarketEngine.API --urls http://localhost:5006

افتح http://localhost:5199، اعمل حساباً، وبتنزل على خطة Observer المجانية.

الترحيلات والزرع

على عقدة وحدة ما بتعمل إشي. Database:MigrateOnStartup افتراضيه true، فاللوحة والـ API بيرحّلوا وبيزرعوا عند الإقلاع.

وعلى أكتر من عقدة، طفّيه — وإلا كل نسخة بتتسابق على Migrate() على نفس القاعدة وقت البدء. حطّ Database__MigrateOnStartup=false على العقد وشغّل الـ migrator مرة كخطوة نشر:

dotnet run --project QuantMarketEngine/src/QuantMarketEngine.Migrator

وهو متكرّر بأمان وبتقدر تعيده.

والزرع بيعمل التلات خطط اللي بتُعرض منها صفحة الأسعار — Observer بـ $0، وAnalyst بـ $49 شهرياً، وDesk بـ $79 شهرياً — مع استحقاقاتها. غيّر سعراً في SaasPlanSeeder وبيتوفّق عند الإقلاع الجاي؛ وصفحة الأسعار وفحوص الاستحقاق بيقرأوا نفس الكتالوج، فما بيقدروا يختلفوا.

النشر

أربع ملفات Dockerfile موجودة بالمستودع.

Dockerfile.bot

المحرّك. بلا فحص صحة.

Dockerfile.dashboard

تطبيق الويب. فحص الصحة :8081/health/live

Dockerfile.api

الـ API العام. فحص الصحة :8080/health/live

Dockerfile.migrator

الترحيلات والزرع. بلا فحص صحة.

ومستضيفا الويب بيعرضوا /health/live (هل العملية شغّالة) و/health/ready (هل بتوصل لقاعدة البيانات) — أشّر فحوص المنسّق عليهم بالترتيب.

وأبسط نشر هو: شغّل الـ migrator مرة، بعدين اللوحة والـ API والبوت، التلاتة و ConnectionStrings__Quant مؤشّر على وحدة تخزين مشتركة وحدة.

حطّ TLS قدّامه

ولا واحد من مستضيفَي الويب بينهي الـ TLS بنفسه. استخدم وكيلاً عكسياً.

الناقص — بصراحة

ما في إشي ناقص من مسار الفوترة — بس ما مرّ عليه بطاقة حقيقية

Checkout المستضاف عند Stripe، وبوابة الفوترة المستضافة، وwebhook بتحقق توقيع بيغطّي التفعيل وتغيّر الحالة والإلغاء — كلهم مبنيين، خلف واجهة مزوّد بتسمح باستبدال بوابة دفع تانية. وبيانات البطاقة ما بتمرّ على المنصة إطلاقاً.

وبيشتغل لحظة ما تنحط Stripe__SecretKey وStripe__WebhookSecret ومعرّفات الأسعار. ولحد ذلك، اللوحة بتكتشف ذلك وبتحوّل الترقيات لتفعيل يدوي، وبتقول ذلك للمستخدم بدل ما تفشل عند خطوة الدفع. واللي بترثه تكامل مبني بلا تاريخ معاملات، مش تكامل غير مبني.

والخطط وفرضها منفصلين عن هاد ومُختبَرين بالكامل: حساب Observer فعلاً بينخدم تقريراً مؤجَّلاً، ونقطة التقرير المؤسسي فعلاً بترد 403 Missing entitlement: reports.institutional.

البريد الصادر

IEmailSender إله تطبيق فارغ للتطوير وتطبيق SMTP بيتفعّل لما ينحط Email:SmtpHost. ولحد ما تحطّه، رسائل استعادة كلمة المرور بتصير أسطر في السجل — يعني مسار الاستعادة ما بيعمل إشي بصمت.

اقرأ بعدها

COMMANDS.md

كل أوامر المحرّك.

configuration.md

كل مفتاح إعدادات بيقراه الكود.

web-publishing-setup.md

ناشر الموقع الثابت.

system-explained/numbers/

ليش كل ثابت في المحرّك إله القيمة اللي إله.