استمرارًا للمقالتين السابقتين حول المسائل التي تحتاج إلى الاهتمام في التداول الآلي:
السلع الجافة الصلبة - تفاصيل وأفكار حول التداول الآلي في الوقت الحقيقي لأنظمة التداول الكمية (1. المشاكل والصعوبات)
نظام التداول الكمي - تفاصيل وأفكار عرض الشركة الآلي (2. الغرض من عرض الشركة)
سنواصل هنا الحديث عن بعض مهارات التعامل التفصيلية في تصميم التعليمات البرمجية.
قاعدة البيانات
كما ذكرنا في المقالتين السابقتين، يعد سجل الحالة لمجموعة استراتيجية أكثر تعقيدًا أمرًا مهمًا للغاية ويتطلب استخدام قاعدة بيانات.
في الواقع، يمكن تصنيف معظم أعمال البرمجة ضمن فئة CRUD، أي أن العملية الأساسية هي فقط إضافة قاعدة البيانات وحذفها وتعديلها والتحقق منها. رموز التداول ليست استثناء. لا يختلف سلوك التداول في السوق الثانوية المتوسطة والمنخفضة التردد كثيرًا في الواقع عما يحدث عندما يذهب الناس للتسوق لشراء كنز معين، فهم يبيعون ويشترون على حد سواء. ويكمن تركيز التداول الآلي في إدارة الوضع الاستراتيجي.
إذا لم تكن قد اعتدت على استخدام SQL كقاعدة بيانات عَلاقية، فمن الأفضل أن تتعلم استخدام Redis كقاعدة بيانات NoSQL في الذاكرة. تتمثل مزاياها في سهولة الاستخدام، والأداء الممتاز، وطبيعتها ذات الخيط الواحد، مما يعني أنك لا تحتاج إلى التفكير في العمليات الأساسية مثل قفل البيانات أثناء القراءة والكتابة. ولكن عيبها هو عدم وجود مفتاح أساسي، مما يعني أنه لا يمكن تحقيق زيادة تلقائية في الأرقام. إذا كنت بحاجة إلى ذلك، فسيتعين عليك كتابة الكود بنفسك. ومع ذلك، لم أستخدم هذه الميزة كثيرًا خلال فترة عملي في التداول الآلي.
هناك سؤال غير مهم وهو أنه إذا كانت قاعدة البيانات في الذاكرة تحتاج إلى تخزين بيانات كبيرة، فإن ذاكرة الخادم يجب أن تكون أكبر قليلاً، ولكن في التداول الكمي الفعلي، فإن 4 جيجابايت، أو حتى 2 جيجابايت من الذاكرة تكفي، ولا تحتاج إلى تخزين كل هذه البيانات.
تتمتع Redis بوظائف قوية جدًا في التداول الفعلي، إذا كانت لديك حاجة للتداول عالي التردد، فإنها تستطيع أيضًا تحقيق نمط الاشتراك والنشر، دون الحاجة إلى إضافة وحدات MQ إضافية. إذا كنت تستخدم قاعدة بيانات MySQL، كما ذكرت سابقًا، إذا لم تكن قد خضت التجربة الفعلية من قبل، فسيتعين عليك استثمار الوقت والجهد لتعلم لغة برمجة SQL التي قد تكون صعبة الفهم. تذكر، نحن هنا للقيام بالتداول الكمي، وليس لتعلم كتابة الكود، لذا حاول استخدام الحلول الأكثر بساطة.
علاوة على ذلك، إذا كنت تقوم بإنشاء مركز بيانات السوق، مع الأخذ في الاعتبار الأداء، يجب أيضًا استخدام قاعدة بيانات Redis، مع دمجها مع طوابير الرسائل لتسهيل الأمور. الحل بسيط، ويصلح لمعظم السيناريوهات. يمكن استخدام خادم واحد (أو عدة خوادم).
تصميم الكود
بشكل أساسي، عند كتابة الكود، هيكل البيانات الأساسي هو الأكثر أهمية. إذا كان تصميم هيكل البيانات غير منطقي، سيصبح الكود معقدًا وصعب الفهم، لأنه يؤدي إلى ترابط غير قابل للتجنب بين الوحدات المختلفة، مما يجعل التعديل أمرًا مزعجًا. لكن لعمل تصميم منطقي، تحتاج إلى بعض الخبرة في التداول، أي خبرة في العمل. كل هذا يتعلق بكيفية كتابة الكود، وليس هناك الكثير لقوله. كل شخص لديه استراتيجيات مختلفة، ولا يمكن معاملتها بالتساوي. ومع ذلك، هناك بعض المبادئ التي يمكن أن تكون مشتركة.
على سبيل المثال، بعد تقديم الطلب، لا يمكن ضمان نجاحه، حتى لو كان أمر سوق، لأنه قد يتم رفضه من قبل البورصة. على سبيل المثال، إذا كانت البورصة مشغولة جدًا، أو كانت هناك مشاكل في الشبكة مثل فقدان الحزم. لذلك، من الأفضل تصميم حالة وسطية مشابهة لتقديم مرحلتين (2 Phase Commit)، إذا لم تنجح، استمر في المحاولة (بالطبع، لا يمكن أن يكون ذلك بت频率 عالية جدًا، وإلا ستتجاوز الحدود وتتعرض للحظر، هناك مخصصات retry decorator لهذا الغرض)، أو إضافة وسائل متعددة لتصحيح الحالة الخاطئة.
في الحالات العامة، حتى لو تعطلت البورصة، فإنها ستستعيد الخدمة في فترة قصيرة. إذا لم يتم استعادة الخدمة، فسيكون هناك دائمًا جهة واحدة معطلة تمامًا، إما البورصة أو خادم الكود الخاص بك. يجب أن تكون حذرًا في المراقبة والتحذير. من المستحيل أن تغطي الأكواد الآلية جميع الجوانب، إذا لم تتم المعالجة تلقائيًا، فقم بإصدار تحذير، وتدخل يدويًا. إذا لم يتم التعامل مع الأمر في الوقت المناسب، طالما أنك لا تفتح صفقات جديدة لزيادة التعرض، فلن تكون هناك مشكلة كبيرة، لأن هناك أوامر وقف الخسارة كضمان.
إليك منطق فتح الصفقة الذي قمت بترتيبه بنفسي:

بشكل عام، رسم مخطط تدفق مشابه، سيجعل كتابة الكود أكثر تنظيمًا، مما يقلل من الأخطاء المحتملة. لذلك، من الأفضل رسم مخطط التدفق قبل كتابة الكود، أو تنظيم الأفكار أثناء الكتابة، حتى يكون التفكير منطقيًا وواضحًا.
منطق إغلاق الصفقة أكثر تعقيدًا بكثير من هذا، لأن هناك شروطًا متعددة للإغلاق، مثل جني الأرباح ووقف الخسارة، وإشارات الخروج من استراتيجيات مختلفة، وكلها ستؤدي إلى إغلاق الصفقة. باختصار، أهمية الخروج تتجاوز أهمية الدخول، وهذا أكثر تعقيدًا في التداول الفعلي.
لأن استراتيجيات الجميع تختلف بلا شك، لن أتحدث عن ذلك أكثر. الأهم هو تنظيم منطق استراتيجيتك، لمنع حدوث أخطاء محتملة.
لكن، كل هذا يبدو معقدًا، لكنه مجرد مجموعة من if else (Python لا تحتوي حتى على عبارة switch). إذا استوفى الشرط، انتقل إلى الخطوة التالية، وإذا لم يكن كذلك، اخرج، لا توجد مشكلة كبيرة.
التحكم في الوقت
عند التداول، بالتأكيد فإن توقيت النقاط مهم جدًا. هنا أود مشاركة بعض تقنيات التحكم في الوقت الخاصة بي.
في البرمجة العامة، إذا كان لديك مهام تحتاج إلى تنفيذها بشكل دوري، فإنك عادةً ما تستخدم حزم مثل apscheduler كجدولة مهام. على سبيل المثال، مهمة مجدولة للتحقق من أسعار k-line عند انتهاء كل ساعة لمعرفة ما إذا كان يجب فتح أو إغلاق الصفقة.
هذا النوع من الحزم يقوم بفتح خيط جديد فورًا عند الوقت المحدد (يمكن تحديده بدقة حتى مستوى الثواني)، ثم تنفيذ المهمة المحددة، المشكلة هي إذا كانت مدة تنفيذ هذه المهمة طويلة جدًا، فإن المهمة التالية ستبدأ على الفور، وهذا سيؤدي إلى وجود مشاكل، رغم أنه يمكن حله، لكنه سيكون مزعجًا.
عادةً ما أستخدم مثل هذه المهام المجدولة عندما أحتاج إلى إخراج معلومات السجل، أو عندما يقوم البرنامج بالتحقق الذاتي. هذه الوظائف تكون منخفضة التردد ويمكن أن تكتمل بسرعة. هذه الوظائف تقرأ فقط البيانات من قاعدة البيانات، ولا تشمل عمليات الكتابة أو التحديث، لذا لا مانع من استخدام apscheduler لفتح خيط آخر لمعالجتها.
ومع ذلك، فإن المنطق الذي يحتاج إلى استعلامات متكررة وبمعدل ت频率 عالي لأسعار السوق، أو حالة الاستراتيجية المحلية، لا يناسب مثل هذه الحزم المجدولة، بل يحتاج إلى استخدام حلقة while للتحكم.
على سبيل المثال، إذا استخدمت استراتيجية تعتمد على إشارات داخل bar k-line، فهذا يعني أن الإشارة قد تظهر في أي وقت، مما يتطلب منك التحقق باستمرار من سعر الهدف أو حجم التداول، لتحديد ما إذا كنت بحاجة إلى تنفيذ العملية المناسبة. إذا كنت بحاجة أيضًا إلى تحكم دقيق في الوقت، مثل تنفيذ منطق فتح الصفقة قبل 10 ثوانٍ فقط من نهاية k-line، فكيف يمكنك ببساطة تحديد ما إذا كانت هذه هي العشر ثوانٍ الأخيرة؟
أنا أفعل ذلك من خلال استخدام الطابع الزمني للحصول على باقي 3600.
(إذا لم تكن معتادًا على الطوابع الزمنية على الكمبيوتر، أي الوقت الذي يبدأ من 1 يناير 1970 بتوقيت غرينتش، على سبيل المثال، الآن هو تقريبًا 1691240298 ثانية، الكمبيوتر دقيق حتى مستوى الميكروثانية وليس هناك مشكلة.)
عند أخذ باقي 3600، إذا كانت النتيجة أكبر من 3590 ثانية، فهذا يعني أنه آخر عشر ثوانٍ. يمكن أن تكون الأخطاء على مستوى الميلي ثانية. كما ذكرت سابقًا، من الأفضل تنفيذ العمليات التجارية الرئيسية في خيط واحد، بشكل متسلسل، وبذلك يمكنك التحكم في الوقت المنفذ بهذه الطريقة، أو بشكل غامض، في فترة زمنية، وهذا سيكون أفضل.
هذا النوع من التحكم يتطلب ضبط توقيت خادمك ليكون قريبًا من الوقت القياسي. ولكن هذا النوع من التحكم لن يكون دقيقًا جدًا، يمكن أن يكون دقيقًا حتى مستوى الثواني. لكن Python لا يمكنها التحكم في الوقت بدقة، لأن تلك الدوال sleep ستؤدي إلى أخطاء تقريبية على مستوى الميكروثانية.
تقليل ت频率 الوصول إلى واجهة برمجة التطبيقات
إذا كنت قد استخدمت مركز بيانات مستقل يعتمد على websocket، ومشاركة معلومات k-line العامة بين استراتيجيات مختلفة، وحتى بين أكواد تداول مختلفة، فلن تكون هناك مخاوف بشأن الحدود المفروضة على ت频率 واجهة برمجة التطبيقات.
لكننا نحتاج أيضًا إلى الحصول على معلومات الحساب في الوقت المناسب، وهي الأموال، والمراكز، والطلبات. على الرغم من أن معلومات الحساب يمكن أيضًا استخدام websocket، في انتظار دفعها من خادم البورصة، ولكن، نظرًا لثلاثة أسباب، من الأفضل استخدام واجهة برمجة التطبيقات بشكل مباشر.
أولاً، المعلومات المرسلة عبر websocket تتطلب من البورصة إعادة الاتصال أو تحديث listenKey (مثل Binance كل 60 دقيقة) لتخفيف العبء. هذا الوضع يتطلب صيانة معقدة. على الرغم من أنه ليس معقدًا جدًا، إلا أنه إذا قمت بإدارة واجهة برمجة التطبيقات، وتداولت عدة حسابات، فسيتعين عليك تشغيل برنامج منفصل لضمان دقة المعلومات في الوقت المناسب، مما يجعل المنطق أكثر تعقيدًا.
ثانيًا، بعض البورصات لا تقدم تحديثات websocket لمعلومات الحساب، في هذه الحالة، تصبح منطق التداول غير قابل للتطبيق، ويصبح نقل الكود الفعلي بين بورصات مختلفة أكثر تعقيدًا. بالطبع، لن يتداول معظم الناس في عدة بورصات، لذا لن يواجهوا مثل هذه الحالات.
ثالثًا، إذا انقطع اتصال websocket، أو واجهت مشاكل مثل تحديث listenKey، أو انتهاء الصلاحية، إذا لم يكتشف البرنامج ذلك في الوقت المناسب، وكان هناك تداول في ذلك الوقت، فإن الاعتماد فقط على websocket قد يؤدي إلى مشاكل كبيرة، لأن معلومات تحديث الحساب التي تم تفويتها لن يتم إرسالها مرة أخرى. لتجنب احتمالية حدوث مثل هذه المشاكل، يجب الاعتماد على واجهة برمجة التطبيقات للحصول على معلومات الحساب المؤكدة. لذا، هل يعني ذلك في النهاية الاعتماد على واجهة برمجة التطبيقات إذًا؟
بناءً على ما سبق، بالنسبة للتداولات متوسطة إلى منخفضة التردد، من الأفضل استخدام واجهة برمجة التطبيقات للحصول على معلومات الحساب. من الأفضل أن تتبع مصدر بيانات موثوق (SSOT) لتجنب الفوضى المحتملة، يمكنك البحث عن هذا المفهوم، فهو مفيد جدًا في البرمجة المعتمدة على معالجة البيانات.
لذا، إذا كنت تستخدم واجهة برمجة التطبيقات فقط للحصول على معلومات الحساب، يمكنك الحفاظ على جدول مؤقت لمعلومات الحساب في الذاكرة المحلية، خاصة بالنسبة للأموال المتاحة، مما يقلل من الزيارات المتكررة إلى البورصة، والأهم من ذلك، أنه سريع، ويمكنك إرسال الطلبات دفعة واحدة، مثل الطلبات الجماعية، ثم معالجة المنطق المتبقي ببطء، وعندما يتم إدخال الطلب بسرعة، فإن الانزلاق سيكون أقل.
علاوة على ذلك، لتحديد ما إذا كانت أوامر وقف الخسارة قد تمت، يمكنك استخدام أسعار high و low الحديثة، بدلاً من الحصول مباشرة على حالة الأوامر المعلقة، لأن تحديد ما إذا كانت الأوامر قد تمت يتطلب استعلامًا متكررًا، مما قد يؤدي إلى الكثير من الزيارات غير الفعالة إلى واجهة برمجة التطبيقات. بينما تحديث أسعار k-line عبر websocket يكون في الوقت المناسب جدًا، ولا يستهلك تردد واجهة برمجة التطبيقات.
بالمثل، يمكن أن تكون بعض أوامر الخروج كذلك. إذا كانت إشارة الخروج الخاصة بك تعتمد على أسعار bar، فإن استخدام أسعار high و low الحديثة قد يكون أفضل. لأنه أحيانًا عندما يتم تنشيط السعر، قد يعود بسرعة، وإذا لم تكن وتيرة الفحص لديك مرتفعة جدًا، فقد تفوت الفرصة. لضمان اتساق استراتيجية التداول، فإن فحص أسعار high و low لن يفوتك الفرصة. كما أن هذه الطريقة لها فوائد إضافية، حيث يمكن أن تكون الأسعار الفعلية للصفقات أكثر ملاءمة مقارنةً بأسعار التفعيل الحقيقية.
لا أعرف ما إذا كانت النقاط المذكورة أعلاه سهلة الفهم. ربما تحتاج إلى بعض الخبرة في التداول الفعلي.
أود أن أشارك بعض الدروس المستفادة من تطوير الكود في التداول الفعلي، آمل أن تكون مفيدة للجميع.