انتقل إلى المحتوى

أساسيات Python للتداول الخوارزمي

يستخدم المتداولون Python لبناء روبوتات التداول والمؤشرات الفنية والإضافات وأدوات أخرى للمساعدة في التداول الخوارزمي أو العمليات اليدوية، ويشرح هذا المقال أساسيات البرمجة في Python.

Python هي لغة برمجة شائعة وعالية المستوى ومتعددة الأغراض تُستخدم في تطوير الويب وعلوم البيانات والأتمتة ومؤخرًا في التداول الخوارزمي في cTrader. سواء كنت تنشئ روبوتات تداول بسيطة تعتمد على الإشارات أو استراتيجيات معقدة متعددة المؤشرات، تجعل Python العملية أسرع وأكثر كفاءة وأسهل في الإدارة.

خوارزميات Python في دقيقة واحدة!

  • استفد من الدعم المدمج لـ Python والوصول السلس إلى واجهة برمجة تطبيقات cTrader Algo، حيث تعد cTrader المنصة الرئيسية الوحيدة التي توفر التكامل الأصلي بشكل مباشر. لا حاجة إلى إضافات أو محولات أو إعدادات معقدة.
  • ركز أكثر على ما يجب أن تحققه استراتيجيتك حيث يصبح المنطق أسهل في التجميع. استمتع بالتكرار السريع والمكونات القابلة لإعادة الاستخدام والأداء القابل للتوسع.
  • طوّر بسرعة وصحح الأخطاء بذكاء وتعاون بدون عوائق. سيكون الكود الخاص بك دائمًا قابلاً للتكيف وموثوقًا وقابلاً للصيانة.
  • الوصول والاستفادة من آلاف المكتبات المجانية/مفتوحة المصدر والبرامج التعليمية المتعمقة وعينات الكود والدعم المجتمعي القوي في جميع أنحاء العالم.

المتغيرات وأنواع البيانات

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

يمكنك إنشاء المتغيرات ببساطة عن طريق تعيين قيمة لها:

1
2
3
4
price = 105.50         # float
enabled = True         # boolean
symbol = "EURUSD"      # string
orders = []            # empty list

أنواع البيانات الشائعة

النوع الوصف مثال
int الأعداد الكاملة 5, 100, api.StepPips
float الأعداد العشرية 1.1234, 100.5, api.Symbol.Ask
bool القيم المنطقية True, False
str السلاسل النصية "EURUSD", api.Label
list مجموعة من العناصر [1, 2, 3], [p for p in api.Positions]

في خوارزميات cTrader، تخزن المتغيرات بيانات مهمة تتعلق بمعلومات السوق وقرارات التداول وتكوين cBot. غالبًا ما تمثل هذه المتغيرات أحجام التداول المحسوبة وقيم المؤشرات والعلامات المنطقية وأسماء الرموز والتسميات والمزيد.

إليك مثال:

1
2
3
4
class GridcBot():
    def on_start(self):
        self.volumeInUnits = api.Symbol.QuantityToVolumeInUnits(api.VolumeInLots)
        self.enoughMoney = True

في هذه الحالة، self.volumeInUnits هو float يستخدم لوضع الأوامر، بينما self.enoughMoney هو bool يتحكم في ما إذا كان سيستمر في فتح المراكز في استراتيجية الشبكة.

الفئات والكائنات

Python هي لغة برمجة كائنية التوجه (OOP)، توفر أدوات لتنظيم الكود من خلال الفئات والكائنات التي تنمذج الكيانات والسلوكيات في العالم الحقيقي. تحدد الفئة مخططًا لإنشاء الكائنات، وهي عبارة عن نسخ من تلك الفئة ذات بيانات وسلوكيات فريدة خاصة بها. المفاهيم الرئيسية موضحة أدناه:

  • يتم تعريف الفئة باستخدام الكلمة المفتاحية class.
  • يتم استخدام طريقة __init__ (المنشئ) بشكل شائع لتهيئة حالة الكائن.
  • تحدد طرق النسخة السلوك.
  • تحتفظ السمات بالبيانات الخاصة بكل كائن.

على سبيل المثال، يمكنك إنشاء فئة Car:

1
2
3
4
5
6
7
class Car:
    def __init__(self, color, speed):
        self.color = color
        self.speed = speed

    def accelerate(self):
        self.speed += 10

ثم يمكنك إنشاء كائنات من فئة Car والتفاعل معها:

1
2
3
my_car = Car("red", 50)
my_car.accelerate()
print(my_car.speed)  # This returns 60

في سياق cTrader

في cTrader، يتم تنفيذ خوارزميات cBot والمؤشرات والإضافات كفئات تغلف منطقها بالكامل. تتفاعل الخوارزميات مع منصة cTrader باستخدام كائن api خاص يوفر الوصول إلى الرموز والرسوم البيانية والأوامر والمؤشرات والمزيد.

يمكنك إنشاء فئة SimplecBot في cTrader بهذه الطريقة:

1
2
3
class SimplecBot():
    def on_start(self):
        api.Print("cBot started")

تتضمن فئة SimplecBot طريقة واحدة (on_start(self)) تنفذ مرة واحدة عند بدء روبوت التداول. تكتب وظيفة api.Print رسالة إلى السجل، مما يوضح كيف تغلف طرق الكائن سلوك التداول.

كل نسخة من الفئة تمثل بشكل فعال خوارزمية تداول مستقلة بذاتها تدير دورة حياتها وقرارات التداول الخاصة بها باستخدام مبادئ البرمجة كائنية التوجه. في cBot القياسي لـ cTrader، قد تخزن الفئة بيانات الحالة الداخلية، مثل الحجم ونتائج المؤشر وما إلى ذلك، وتستخدم عدة طرق لإدارة منطق التداول.

ضع في اعتبارك هذا المثال لتقاطع MACD الذي يهيئ مؤشر MACD عند البدء ويضع أمر شراء عند حدوث إشارة تقاطع MACD:

1
2
3
4
5
6
7
class MACDCrossover():
    def on_start(self):
        self.macd = api.Indicators.MacdCrossOver(api.Source, api.LongCycle, api.ShortCycle, api.SignalPeriods)

    def on_bar_closed(self):
        if self.macd.MACD.Last(0) > self.macd.Signal.Last(0):
            api.ExecuteMarketOrder(TradeType.Buy, api.SymbolName, volume)

الكلمة المفتاحية self

داخل الفئة، يشير self إلى نسخة الفئة ويوفر الوصول إلى متغيراتها وطرقها. يتم استخدام self لـ:

  • تخزين البيانات مثل نتائج المؤشر وأحجام المراكز وقيم التكوين.
  • تتبع الحالة الداخلية عبر استدعاءات الطرق المختلفة مثل on_start() أو on_tick() أو on_bar_closed().

يوضح هذا المثال كيفية استخدام self:

1
2
3
4
class MACDCrossover():
    def on_start(self):
        self.volume = api.Symbol.QuantityToVolumeInUnits(api.VolumeInLots)
        self.macd = api.Indicators.MacdCrossOver(api.Source, api.LongCycle, api.ShortCycle, api.SignalPeriods)

في هذه الحالة، يخزن self.volume الحجم المحسوب لاستخدامه في الصفقات، بينما يخزن self.macd كائن مؤشر MACD، مما يسمح بالوصول إلى قيمه طوال دورة حياة cBot. بدون self، ستكون هذه المتغيرات محلية لـ on_start() ولا يمكن الوصول إليها في on_bar_closed() حيث قد يتم اتخاذ قرارات التداول.

الأساليب والدوال

الدوال هي كتل من التعليمات البرمجية القابلة لإعادة الاستخدام والمستخدمة لأداء مهمة محددة. عندما يتم تعريف الدوال داخل فئة، تسمى أساليب، وهي تعمل على الحالة الداخلية للفئة عبر المعامل self. في سياق التداول الخوارزمي باستخدام cTrader، تحدد الأساليب كيفية تصرف خوارزمية التداول الخاصة بك في ظل ظروف مختلفة، مثل نقرات السوق أو إغلاق الشموع أو التهيئة.

تقوم المنصة تلقائيًا باستدعاء أساليب خاصة (تُعرف باسم معالجات الأحداث) في الأوقات المناسبة، مثل:

on_start(self) – يتم استدعاؤها مرة واحدة عند تهيئة الروبوت.

1
2
def on_start(self):
    api.Print("cBot started")

on_tick(self) – يتم استدعاؤها عند كل نقرة سوق واردة.

1
2
def on_tick(self):
    api.Print("Tick received")

on_bar_closed(self) – يتم استدعاؤها عند إغلاق شمعة/عمود جديد (مثالي للاستراتيجيات المعتمدة على الوقت).

1
2
3
4
def on_bar_closed(self):
    if Functions.HasCrossedAbove(...):
        self.close_positions(TradeType.Sell)
        api.ExecuteMarketOrder(...)

الأساليب المخصصة أو المساعدة

لتجنب إعادة كتابة وتكرار التعليمات البرمجية، يمكنك تعريف أساليبك الخاصة. يجب أن تغلف هذه الأساليب المخصصة المنطق المشترك الذي قد يعيد روبوت التداول الخاص بك استخدامه.

يعيد الأسلوب أدناه جميع المراكز المفتوحة المرتبطة بتسمية cBot الحالي، مما يسهل تتبع أو إغلاق المراكز بشكل متسق.

1
2
def get_bot_positions(self):
    return api.Positions.FindAll(api.Label)

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

1
2
def get_distance_in_pips(self, position):
    return (position.EntryPrice - api.Symbol.Ask if position.TradeType == TradeType.Buy else api.Symbol.Bid - position.EntryPrice) / api.Symbol.PipSize

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

على سبيل المثال، بعد تعريف get_bot_positions()، قد تستدعيه داخل أسلوب آخر لإغلاق جميع المراكز:

1
2
3
4
def close_positions(self, tradeType):
    for position in self.get_bot_positions():   # Helper method call
        if position.TradeType == tradeType:
            api.ClosePosition(position)

هنا، self.get_bot_positions() يستدعي الأسلوب المساعد، ويعيد فقط المراكز المرتبطة بتسمية الروبوت الحالي.

وبالمثل، يمكن إعادة استخدام أسلوب get_distance_in_pips() لاتخاذ قرارات التداول. على سبيل المثال، في استراتيجية الشبكة، قد ترغب في فتح أمر جديد فقط إذا تحرك السوق بعيدًا بما فيه الكفاية عن آخر مركز:

1
2
3
4
5
6
7
def on_tick(self):
    grid_positions = self.get_bot_positions()
    if len(grid_positions) > 0:
        position_with_highest_pips = sorted(grid_positions, key=lambda pos: pos.Pips, reverse=True)[0]
        distance = self.get_distance_in_pips(position_with_highest_pips)  # Helper method call
        if distance >= api.StepPips:
            self.open_position()

مفاهيم أخرى

مساحات الأسماء والاستيرادات

تساعد مساحات الأسماء في Python على تنظيم التعليمات البرمجية وتجنب تضارب الأسماء عن طريق تجميع المعرفات ذات الصلة (المتغيرات والدوال والفئات). تسمح الاستيرادات بجلب الوحدات أو المكتبات أو الحزم الخارجية لتوسيع الوظائف إلى ما وراء ميزات Python القياسية.

في Python، يمكنك استيراد الوحدات القياسية أو الحزم المخصصة:

1
2
import math
print(math.sqrt(16))  # 4.0

يمكنك أيضًا استيراد دوال أو فئات محددة:

1
2
from math import sqrt
print(sqrt(25))  # 5.0

عند كتابة الخوارزميات لـ cTrader في Python، تعمل الاستيرادات بشكل مختلف قليلاً لأن cTrader يتكامل مع واجهة برمجة التطبيقات cTrader Algo المستندة إلى .NET. يتضمن هذا الإعداد جسرًا يسمح لكود Python بالتفاعل مع تجميعات .NET، وهذا يفسر سبب بدء cBot النموذجي بـ:

1
2
3
4
5
6
7
8
9
import clr

clr.AddReference("cAlgo.API")

# Import cAlgo API types
from cAlgo.API import *

# Import trading wrapper functions
from robot_wrapper import *

حيث:

  • import clr يحمل Common Language Runtime (CLR)، مما يتيح لـ Python التشغيل البيني مع مكتبات .NET.
  • clr.AddReference("cAlgo.API") يشير إلى تجميع واجهة برمجة تطبيقات cAlgo، والذي يحتوي على جميع الفئات والأساليب والكائنات المتعلقة بالتداول.
  • from cAlgo.API import * يجلب جميع الأنواع من واجهة برمجة التطبيقات (مثل TradeType و Color و Position).
  • from robot_wrapper import * يحمل الدوال المساعدة والأغلفة التي تبسط عمليات التداول الشائعة.

يضمن نظام مساحة الأسماء أن:

  • أسماء Python المدمجة (مثل print) تتجنب التعارض مع أسماء cTrader (مثل api.Print).
  • جميع الكائنات الخاصة بالتداول (api.Symbol و api.Indicators و api.ExecuteMarketOrder) تأتي من مساحة أسماء واجهة برمجة تطبيقات cTrader Algo، مما يبقيها منظمة ومميزة.
  • يمكنك توسيع الوظائف عن طريق استيراد حزمك المخصصة للعمل جنبًا إلى جنب مع الأنواع المدمجة في cTrader.

الحلقات

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

في Python، التكرار بسيط ويدعم كلاً من حلقات for (للتكرار خلال المجموعات) وحلقات while (للتكرار حتى يتغير الشرط).

يمكنك كتابة حلقة لإغلاق المراكز:

1
2
3
4
def close_positions(self, tradeType):
    for position in self.get_bot_positions():
        if position.TradeType == tradeType:
            api.ClosePosition(position)

في هذه الحالة:

  • self.get_bot_positions() يعيد جميع المراكز التي تنتمي إلى cBot الحالي.
  • تمر حلقة for عبر كل مركز.
  • يتم إغلاق المراكز المطابقة باستخدام api.ClosePosition.

فكر في حلقة مختلفة تتكرر عبر جميع مراكز الشبكة المفتوحة، وتغلقها واحدة تلو الأخرى وتتحقق بشكل متكرر مما إذا كانت هناك أي مراكز متبقية (للأمان في حالة الإغلاقات الجزئية).

1
2
3
4
5
6
def close_grid_positions(self):
    for position in self.get_grid_positions():
        position.Close()

    if len(self.get_grid_positions()) > 0:
        self.close_grid_positions()

العبارات الشرطية

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

تدعم Python ثلاثة تراكيب شرطية رئيسية:

  • if – ينفذ التعليمات البرمجية عندما يكون الشرط صحيحًا.
  • elif (else if) – يتحقق من شرط آخر إذا كان الشرط الأول خاطئًا.
  • else – يوفر حلاً بديلاً عندما لا يتم استيفاء أي شروط.

فكر في شروط تقاطع MACD المعبر عنها في هذا الكود:

1
2
3
4
if self.macd.MACD.Last(0) > self.macd.Signal.Last(0):
    api.ExecuteMarketOrder(TradeType.Buy, api.SymbolName, self.volumeInUnits, api.Label, api.StopLossInPips, api.TakeProfitInPips)
elif self.macd.MACD.Last(0) < self.macd.Signal.Last(0):
    api.ExecuteMarketOrder(TradeType.Sell, api.SymbolName, self.volumeInUnits, api.Label, api.StopLossInPips, api.TakeProfitInPips)

هذا الهيكل:

  • يضع أمر شراء عندما يكون خط MACD فوق خط الإشارة.
  • يضع أمر بيع عندما يكون خط MACD تحت خط الإشارة.
  • يتخطى التداول إذا لم تكن هناك إشارة واضحة.

القوائم

توفر Python أدوات قوية وموجزة للعمل مع مجموعات البيانات، مثل الصفقات أو الأسعار أو المؤشرات. اثنان من الأكثر شيوعًا هما فهم القائمة ودوال التجميع المدمجة مثل sum().

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

1
2
numbers = [1, 2, 3, 4, 5]
squares = [n**2 for n in numbers if n % 2 == 0]

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

1
positions = [p for p in api.Positions if p.SymbolName == api.SymbolName]

يعد تجميع الربح أو الحجم باستخدام sum() نهجًا قياسيًا في استراتيجيات تداول الشبكة لتحديد متى يلبي إجمالي الربح الهدف.

1
net_profit_sum = sum([p.NetProfit for p in grid_positions])

يمكنك الجمع بين التصفية والتجميع بهذه الطريقة:

1
2
buy_positions = [p for p in api.Positions if p.TradeType == TradeType.Buy]
avg_buy_price = sum([p.EntryPrice * p.VolumeInUnits for p in buy_positions]) / sum([p.VolumeInUnits for p in buy_positions])

العمليات المتزامنة وغير المتزامنة

تتبع Python نموذج تنفيذ متزامن بشكل افتراضي. يتم تشغيل أساليب دورة حياة الخوارزمية الخاصة بك، مثل on_start و on_tick و on_bar_closed، بواسطة حلقة أحداث المنصة، واحدة تلو الأخرى.

فيما يلي تمثيل أساسي لتدفق التداول المتزامن:

graph TD
    A(مواجهة إشارة) ==> B(تنفيذ أمر)
    B ==> C(يصل المركز إلى TP/SL)
    C ==> D(إغلاق المركز)
    D ==> A

في التدفقات المتزامنة، يمكن لأي عملية طويلة الأمد أن تحظر الخيط، مما قد يؤخر الحدث التالي. ومع ذلك، في استراتيجيات العالم الحقيقي، تبدأ الخوارزميات عادةً إجراءات غير حاجبة، مثل تقديم أمر أو تعيين إيقاف الخسارة وجني الأرباح، ثم تنتظر الأحداث المستقبلية (مثل نقرة أو شمعة جديدة) للتعامل مع النتائج. ينتج عن هذا الإعداد تنسيق شبيه بغير المتزامن، على الرغم من عدم استخدام بنيات Python async/await.

ملاحظة

بالنسبة لمعظم خوارزميات التداول، البرمجة غير المتزامنة الصريحة غير ضرورية.

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

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

graph TD
    A([مقابلة إشارة]) ==> B([فتح أمر شراء]) & C([فتح أمر بيع للتحوط]) ==> D([يصل أحد الأوامر إلى جني<br> الأرباح أو وقف الخسارة])
    D ==> E([إغلاق الصفقة])
    E ==> A

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

Image title