FakeTradingSession_—_عقد_الولاء_لـ_cTrader_Open_API
tests/UnitTests/CopyTrading/FakeTradingSession.cs=IOpenApiTradingSession
في_الذاكرة_التي_تُشغَّل_عليها_جميع_اختبارات_وحدة_نسخ_التداول.المهمة:
تقليد خادم_cTrader_Open_API_الحقيقي بدرجة_كافية_لأن_اختبارات
الوحدة_تغطي_السلوك_الذي_كان_فقط_المستوى_المباشر_يستطيع_التقاطه.هذا
التوثيق_=_عقد_الولاء:_ماذا_ينمذئ_الأصلي،_بأي_ولاء،والقاعدة
التي_تحافظ_على_الأصلي.
قاعدة_ملزمة_(CLAUDE.md): الأصلي_يبقى_مؤمناً_بـ_cTrader. مدّده،_ولاتُضعفه _لتمرير_اختبار.كل_سلوك_حقيقي_جديد تعتمد_عليه_يتم_نمذجته_هنا، _مُثبّت_باختبار_الولاء.
مصفوفة_الولاء_(F1–F13)
تتتبع_خطة_plans/copy-trading-overhaul.md §7.6.الأسطورة:✅نمذأ·◑جزئي
(opt-in/تمديد)·_⬜_لم_يُنمذأ_بعد.
| # | سلوك_Open_API_الحقيقي | حالة_الأصلي | كيف_يتم_نمذجته |
|---|---|---|---|
| F1 | أمر_السوق_يمكنه ملء_جزئي | ◑ | PartialFillFractionForCtid[ctid]_=_f ملء_فقطf×volume؛المصادقة |
| تُظهر_الفجوة_ Phase-1 true-up (G5) _يُغلق._قبول→زوج_الملء_لا_يزال_قادماً. | |||
| F2 | الحجم_يُطبع_إلى step،_مرفوض_أقل_من min /_أكثر_من max | ✅ | VolumeBoundsForCtid[ctid]_=_(Step,_Min,_Max) يُطبع_إلى_الأقل_إلى |
step،يُلقيCtraderRejectException(VolumeTooLow/High). | |||
| F3 | SL/TP_غير_صالح مرفوض_(الجانب_+_الأرقام) | ⬜ | مخطط_في_ Phase 0a/1 (أزواج_مع_M6 _تطبيع_دقة_SL/TP). |
| F4 | الأسعار مُوسَّعة_صحيحياً_بالأرقام؛_pipPosition | ◑ | SymbolDetails الآن_تحملDigits(و_MaxVolume)_،مُعبأة_من_الرمز |
الحقيقي؛_PipPosition تدفع_تحمل_نطاق_السوق،Digits تدفع | |||
| تطبيع_دقة_SL/TP_(M6)._توسع_السعر_الصحيح_العدد_لا_يزال_معلقاً. | |||
| F5 | نطاق_السوق الملء_فقط_إذا_كانت_الأسعار_الفورية_ضمنbase_±_slippage، | ||
| وإلا_مرفوض | ✅ | IsMarketRangeRejected يقارن_السعر_الفوري(SetSpot)إلى | |
baseSlippagePrice_±_slippageInPoints.علامةRejectMarketRangeForCtid_ | |||
| القديمة_تstill_تُجبر_الرفض. | |||
| F6 | مُحرّك_معلق_→_ملء حدث_ثنائي(الأمر_يحمل_positionId_+موضع OPEN) | ◑ | PushOpen(...,_orderId:) يُعيد_إنتاج_حدث_الملء_المعلق؛ |
| FX-Blue/cMAM_إلغاء_النسخ_المزدوج_مغطى_في_ | |||
CopyEngineHostTests.Filled_pending_does_not_double_open. | |||
| F7 | إقفال_مُحرّك_بالخادم (SL/TP_أُصيب،_إيقاف_الخسارة) | ⬜ | اليوم_اختبار_مدفوع_(PushClose)؛SL/TP_المُحرّك_بالسعر+_ |
| إقفالات_ stop-out_مخططة. | |||
| F8 | لكل_حساب _جداول/تفاصيل_الرموز | ◑ | أسماء/معرفات_الرموز_لكل_أصلي؛الجداول_المتباينة_لكل_حساب |
| (cross-broker)_معلقة. | |||
| F9 | حالة_الحساب_الكاملة (الرصيد،_الحقبة،_الهامش،_الرصيد_الحر) | ◑ | Balance=LoadPositionValuationsAsync(entry/swap/عمولة_عبر |
SetPositionValuation)+SetSpot تغذي_الحقبة_الفعلية_إلى | |||
| تحديد_الحجم_النسبي_للحسابات_(G2،اختبار_بالوحدة_في | |||
CopyEquitySizingTests).الهامش_المستخدم_لا_يعرضه_واجهة_البرمجة | |||
| للمصادقة_،_لذا_الرصيد_الحر_يُبلغ_كـ_الحقبة. | |||
| F10 | الأحداث_تحمل طوابع_الوقت_للخادم | ✅ | ExecutionEvent.ServerTimestamp(unix_ms)—الجلسة_الحقيقية_تقرأ |
من_deal's_ExecutionTimestamp؛PushOpen/PushPending ت acceptserverTimestamp: | |||
ل drive_FakeTimeProvider-_الاختبار_يقود_زمن_ال | latency_النسخ_الحقيقي_(G1). | ||
| F11 | وضع_التداول_/_الجدول (معطل_/close-only/_مغلق) | ⬜ | Phase 2b_مخطط. |
| F12 | تصنيف_الأخطاء_المكتوبة (ProtoOAErrorRes أكواد) | ✅ | RejectReasonForCtid[ctid]_=_CtraderRejectReason.X تُلقي |
CtraderRejectException(reason)_(NotEnoughMoney،_MarketClosed، | |||
| PositionNotFound،_…). | |||
| F13 | إلغاء_الرمز —رمز_قديم→خطأ auth | ✅ | InvalidateToken(ctid) يُعلم_الرمز_المُرفق_قديم؛ |
استدعاءات_التداول_تُلقي حقيقي OpenApiException مع | |||
OpenApiErrorKind.TokenInvalid(رمزCH_ACCESS_TOKEN_INVALID)_، | |||
تماماً_مثل_الخادم_الحقيقي_،حتىSwapAccessTokenAsync يثبت | |||
| رمزاً_حديثاً._يُغذي_اختبار_متانة_الرمز_M1. |
اختبارات_الولاء_تعيش_في_
tests/UnitTests/CopyTrading/FakeTradingSessionFidelityTests.cs.
opt-in،_الافتراضيات_تحافظ_على_السلوك_القديم
كل_مقبض_ولاء معطل_افتراضياً لذا_الأصلي_يحافظ_على_سلوك_ملء_بسيط للاختبارات_التي_لا_تهتم._الاختبار_يختار_لكل_حساب:
session.VolumeBoundsForCtid[slave] = (Step: 10, Min: 10, Max: 1000); // F2
session.PartialFillFractionForCtid[slave] = 0.6; // F1 / G5
session.RejectReasonForCtid[slave] = CtraderRejectReason.NotEnoughMoney; // F12 (one-shot)
session.InvalidateToken(slave); // F13
التوصيف_والمطابقة_(مخطط،يحافظ_على_الأصلي≡_الحقيقي)
آليتان_تحافظان_على_الأصلي_مقابل_الخادم_الحقيقي_المتحرك_(مُتتبع،تهبط عبر_ Phase 0a):
- توصيف_مباشر (
LiveApiCharacterization،_حسابات_demo،_محصور_بالأسرار،Inconclusiveعند_السوق_المغلق):تقود_Open_API_الحقيقي، تسجل_الحقيقة_السلكية_الفعلي(تسلسلات_الأحداث،_التوسع،أكواد الرفض)في_ثوابت_ذهبية_مُفحصة_في_مشروع_الاختبار.لا_أسرار_في الثوابت—_فقط_الأشكال_المُراقَبة. - harness_المطابقة:تشغل_مجموعة_السيناريو_نفسه_مرتين—مرة
ضد_
FakeTradingSession،ومرة_ضد_الجلسة_الحقيقية(عندما_الأسرار_ موجودة)_—تؤكد_نتائج_ملاحظة_متطابقة.الخادم_الحقيقي_يتغير→ الساق_الحية_تفشل→تحديث_الأصلي.هذا_يجعل"اختبارات الوحدة_تغطي_كل_شيء"_موثوقاً.
الأسرار_الحية:secrets/dev-credentials.local.json(أو_الملفات_المنقسمة_
القديمة)_—انظرdocs/testing/dev-credentials.md.