دورة البرمجة في الفوركس للمبتدئين – الدرس الثامن

ضمن دورة البرمجة يمكن أيضًا استخدام OrderModify () لتعديل سعر الأمر لأمر معلق. إذا كان الأمر معلقًا يتم الوصول إلى السعر بالفعل ويتم تنفيذ الأمر ، ولم يعد أمرًا معلقًا والسعر لا يمكن تغييره.

سنستخدم المتغير NewPendingPrice لتمثيل سعر الأمر الذي تم تغييره. سنفترض السعر
تم حسابه بالفعل وهو صالح. إليك كيفية تعديل سعر الأمر المعلق:

كود:

OrderSelect(Ticket,SELECT_BY_TICKET);
if(NewPendingPrice != OrderOpenPrice())
{
bool TicketMod = OrderModify(Ticket,NewPendingPrice,OrderStopLoss() ,
OrderTakeProfit(),0);
}

كما هو الحال دائمًا ، نقوم باسترداد معلومات الطلب باستخدام OrderSelect (). بهذه الطريقة يمكننا تمرير
أسعار وقف الخسارة وجني الأرباح غير المتغيرة لوظيفة OrderModify (). قبل تعديل الطلب ،
سوف نتحقق من أن سعر الأمر المعلق الجديد ليس هو نفسه السعر الحالي سعر الأمر المعلق.

بالنسبة لـ OrderModify () ، نحدد بطاقة الطلب ، وسعر الطلب الجديد المخزن في NewPendingPrice ،
وقيم وقف الخسارة وجني الأرباح التي لم تتغير والتي يمثلها OrderStopLoss () و OrderTakeProfit ().
نحن لا نستخدم وقت انتهاء صلاحية لهذا الطلب ، لذلك نستخدم المعامل 0 لانتهاء الصلاحية.

التحقق من التوقفات وأسعار الطلبات المعلقة

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

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

لهذه الأسباب ، من الضروري دائمًا التحقق من وقف الخسارة أو جني الأرباح أو سعر الأمر المعلق
صالح ، وليس قريبًا جدًا من سعر السوق الحالي. نتحقق من ذلك عن طريق التحقق من stop
level للعملة.

مستويات التوقف Stop Levels

مستوى الإيقاف هو عدد النقاط بعيدًا عن سعر العرض أو الطلب الحالي الذي تتوقف فيه جميع نقاط التوقف والمعلقة التي
يجب وضع الطلبات عندها. بالنسبة لمعظم الوسطاء ، يبلغ مستوى التوقف حوالي 3-4 نقاط. وسطاء ECN
بشكل عام ، تكون مستويات التوقف ضيقة للغاية ، بينما يمتلك الوسطاء الآخرون مثل Alpari مستويات توقف أوسع (عند
8 نقاط على الأقل).

توضح الصورة مستويات التوقف فيما يتعلق بالأسعار. فكر في
السعر ليس مجرد قيمة واحدة (مثل العطاء Bid) ، ولكن
بدلا منرذلك خط سميك من عرض السبريد.

اضغط على الصورة لعرض أكبر. 

الإسم:	SumatraPDF_pHFteZdjYy.png 
مشاهدات:	52 
الحجم:	103.6 كيلوبايت 
الهوية:	949472

توجد حدود على جانبي خط السعر هذا ، يُشار إليها بمستويات التوقف
stop levels. يجب وضع جميع أوامر وقف الخسارة وجني الأرباح والمعلقة
خارج هذه الحدود.

دالة MarketInfo () ذات المعلمة MODE_STOPLEVEL هي
تستخدم لاسترداد مستوى الإيقاف لرمز العملة. مستوى التوقف
يتم التعبير عنها كرقم صحيح ، وستحتاج إلى تحويلها إلى
قيمة كسرية باستخدام النقطة Point.

بالنسبة لعملة مكونة من 4 أرقام بمستوى إيقاف قدره 3 نقاط ، تعمل MarketInfo () مع MODE_STOPLEVEL
3. بالنسبة للعملة المكونة من 5 أرقام بمستوى توقف قدره 3 نقاط ، سيرجع MarketInfo () 30 ، بسبب
المكان العشري الإضافي. إليك رمز استرداد مستوى الإيقاف وتحويله إلى قيمة رقم عشري:

كود:

double StopLevel = MarketInfo(Symbol(),MODE_STOPLEVEL) * Point;

لاحظ أننا نستخدم متغير النقطة Point المحدد مسبقًا ، بدلاً من وظيفة PipPoint () التي أنشأناها
سابقًا. هذا لأننا نحتاج إلى ضرب مستوى التوقف في قيمة النقطة الفعلية. لأربعة أرقام
العملة ، ستكون النقطة 0.0001 ، وللعملة المكونة من 5 أرقام ، ستكون النقطة 0.00001. إذا توقف
المستوى 3 نقاط كما هو موضح أعلاه ، فإن القيمة الكسرية ستكون 0.0003.

الآن وقد توصلنا إلى كيفية إيجاد مستوى التوقف ، نحتاج إلى حساب الحد الأدنى و
الحد الأقصى لقيم وقف الخسارة وجني الأرباح وأسعار الأوامر المعلقة. نقوم بذلك عن طريق إضافة أو
طرح مستوى الإيقاف من أسعار العرض والطلب الحالي.

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

إذا كان سعر الطلب 1.4650 ، ومستوى StopLevel هو 0.0003 نقطة كما هو محسوب أعلاه ، فعندئذ يكون الحد الأدنى
سعر مستوى الإيقاف 1.4653. إذا كنا نضع أمر جني أرباح شراء بهذا الأمر ، فيجب أن يكون كذلك
فوق هذا السعر. سنسمي هذا المستوى UpperStopLevel ، لأنه أعلى من السعر.

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

كود:

double LowerStopLevel = Bid - StopLevel;

سوف نسمي هذا LowerStopLevel ، لأنه أقل من السعر. قبل تقديم الطلب ، استخدم
قيم UpperStopLevel و LowerStopLevel أعلاه للتحقق من وقف الخسارة وجني الأرباح والتعليق على
أسعار الطلبات. ضع في اعتبارك أن الأسعار يمكن أن تتغير بسرعة ، وستحتاج إلى التوقفات الفعلية والأرباح
والأوامر المعلقة لتكون خارج هذه المستويات.

Verifying Stop Loss and Take Profit Prices

الحد الأدنى لجني الأرباح بالنقاط سيكون مساويًا لسعر فتح الأمر زائد أو ناقص مستوى الإيقاف.
إذا كان مستوى الإيقاف 3 نقاط ، وسعر فتح الأمر هو 1.4500 ، فإن سعر جني الأرباح لأمر الشراء
يجب أن يكون أعلى من 1.4503.

ومع ذلك ، فإن الحد الأدنى لإيقاف الخسارة بالنقاط لأمر السوق سيشمل السبريد الحالي ، وبالتالي فإن
الحد الأدنى لإيقاف الخسارة أكبر من الحد الأدنى لجني الأرباح. على سبيل المثال ، إذا كان مستوى التوقف هو 3
نقطة ، الفارق هو 2 نقطة ، وسعر فتح الأمر 1.4500 ، وقف الخسارة لأمر شراء في السوق
يجب أن يكون أقل من 1.4495.

هذا لا ينطبق على الأوامر المعلقة ، لذلك عند التحقق من وقف الخسارة لأمر معلق ، فإنه ليس
ضروري للرقم في السبريد. لذلك إذا كنت تقوم بوضع أمر معلق عند 1.4500 ، وكان مستوى الإيقاف
3 نقاط ، إذن يمكن وضع وقف الخسارة في أي مكان أقل من 1.4497.

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

كود PHP:

double MinStop = 5 * UsePoint;
if(BuyStopLoss > LowerStopLevel) BuyStopLoss = LowerStopLevel - MinStop;
if(BuyTakeProfit < UpperStopLevel) BuyTakeProfit = UpperStopLevel + MinStop; 




يضيف المتغير MinStop أو يطرح 5 نقاط من مستوى التوقف ، لضمان ان أسعارنا التي تم التحقق من صحتها
لا تصبح غير صالحة بسبب الانزلاق. يمكنك ضبط هذه القيمة لفرض حد أدنى كافٍ
لمستوى وقف / ربح ، أو حتى استخدام متغير خارجي لضبط هذا المبلغ.

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

للتحقق من وقف الخسارة وجني الأرباح لأمر بيع ، نقوم ببساطة بعكس الحسابات:

كود PHP:

if(SellTakeProfit > LowerStopLevel) SellTakeProfit = LowerStopLevel - MinStop;
if(SellStopLoss < UpperStopLevel) SellStopLoss = UpperStopLevel + MinStop; 

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

كود PHP:

if(BuyStopLoss > LowerStopLevel)
{
Alert("The stop loss setting is too small!");
return(0);

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

سنقوم تلقائيًا بتعديل الأسعار غير الصالحة ، بافتراض الأفضل
ضع أمرًا مصححًا بدلاً من عدم تقديمه على الإطلاق. قد يكون من المفيد توثيق وقت حدوث ذلك
عن طريق طباعة رسالة إلى السجل ( log ):

كود PHP:

if(BuyStopLoss > LowerStopLevel)
{
BuyStopLoss = LowerStopLevel - MinStop;
Print("Stop loss is invalid and has been automatically adjusted");

التحقق من أسعار الطلبات المعلقة
Verifying Pending Order Prices

إليك كيفية التحقق من سعر الأمر المعلق لإيقاف الشراء أو أمر حد البيع. السعر المعلق
مخزن متغيرة سعر الطلب المعلق لدينا:

كود PHP:

if(PendingPrice < UpperStopLevel) PendingPrice = UpperStopLevel + MinStop; 

لاحظ أن المنطق هنا مطابق للرمز أعلاه الذي يتحقق من جني الأرباح والشراء وإيقاف البيع
لأسعار الخسارة. وإليك الكود للتحقق من سعر الأمر المعلق لإيقاف البيع أو أمر حد الشراء:

كود PHP:

if(PendingPrice > UpperStopLevel) PendingPrice = UpperStopLevel – MinStop; 

Calculating Lot Size

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

يعتبر الإفراط في الرافعة المالية أحد أكبر أسباب خسارة تجار الفوركس. استخدام أحجام كبيرة جدًا بالنسبة إلى اللوتات
إلى رأس المال الخاص بك يمكن أن يمحو حسابك بنفس السهولة التي يمكن أن ينتج عنها مكاسب كبيرة.
أوصيك بألا تستخدم أكثر من 2-3٪ من حقوق الملكية الخاصة بك لكل صفقة. بهذا ، فإننا نعني أن
الحد الأقصى للمبلغ الذي يمكن أن تخسره في كل صفقة لن يكون أكثر من 2-3٪ من حسابك.

إدارة الأموال
Money Management

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

كود PHP:

extern double EquityPercent = 2;
extern double StopLoss = 50; 

أولاً ، نحتاج إلى حساب مقدار السيولة المحددة بواسطة EquityPercent. إذا كان لدينا حساب
10،000 دولار أمريكي ، ونستخدم 2٪ من السيولة لدينا ، فسيكون الحساب كالتالي:

كود PHP:

double RiskAmount = AccountEquity() * (EquityPercent / 100); 

AccountEquity () هي دالة MQL تُرجع رصيد الحساب الجاري. نحن نقسم
النسبة المئوية بمقدار 100 تعطينا قيمة كسرية (0.02). ثم نضرب ذلك في
AccountEquity () لحساب مقدار السيولة للاستخدام. 2٪ من 10،000 دولار هي 200 دولار ، وهذا سيكون
مخزن في متغير RiskAmount.

بعد ذلك ، علينا إيجاد قيمة التك. هذا هو الربح لكل نقطة إذا كنا نتداول في لوت واحد من العملة المطلوبة
على سبيل المثال ، إذا كنا نتداول 1 لوت من اليورو مقابل الدولار الأميركي على حساب قياسي (100 ألف لوت) ، فإن الربح

لكل نقطة 10 دولارات. في الحساب المصغر (10 آلاف لوت) ، سيكون الربح لكل نقطة 1 دولار.
يمكننا استخدام وظيفة MarketInfo () مع المعلمة MODE_TICKVALUE لإرجاع الربح لكل
نقطة للعملة المحددة. يجب أن تكون قيمة العلامة بالنقاط ، لذلك إذا كنا نتداول على نقطة كسرية
الوسيط (3 أو 5 منازل عشرية) ، يجب علينا مضاعفة قيمة التجزئة في 10.

كود PHP:

double TickValue = MarketInfo(Symbol(),MODE_TICKVALUE);
if(Point == 0.001 || Point == 0.00001) TickValue *= 10; 

بافتراض أننا نتداول في حساب قياسي ، فإن قيمة التك لليورو مقابل الدولار الأميركي ستكون 10. سيتم تخزين هذا
في متغير TickValue. إذا كان هذا وسيطًا بنظام النقطة الكسرية ، فستكون قيمة TickValue هي 1. سنحتاج إلى ذلك
ضرب هذا في 10 ليصبح مكافئًا لنقطة واحدة. إذا كان متغير النقطة Point يشير إلى أن العملة
هي 3 أو 5 منازل عشرية ، سيتم ضرب TickValue في 10 لجعلها تساوي 2 أو 4 عشرية.

الخطوة التالية هي حساب حجم اللوت. أولاً ، نقسم مقدار المخاطرة على إعداد StopLoss.
سيعطينا هذا ربحنا لكل نقطة لهذا الطلب. 200 دولار أمريكي مقسومة على وقف الخسارة عند 50 ستحصل على 4 دولارات أمريكية.
الآن كل ما يتعين علينا القيام به هو قسمة ذلك على TickValue للحصول على حجم اللوت:

كود PHP:

double CalcLots = (RiskAmount / StopLoss) / TickValue; 

سيكون حجم اللوت المحسوب على الحساب القياسي 0.4 لوت. على الحساب المصغر ، اللوت المحسوب
سيكون الحجم 4 عقود. يتم تخزين هذه القيمة في متغير CalcLots.

إذا كنت تستخدم إدارة الأموال المناسبة ، فستكون النسبة المئوية لرأس المال الذي تستخدمه عادة
ثابتة. (1-2٪ للمخاطر المتحفظة ، 5٪ للمخاطر الأعلى). وقف الخسارة الخاص بك ، من ناحية أخرى ،
ستختلف بناءً على الإطار الزمني الخاص بك ونظام التداول الخاص بك. حجم اللوت سوف يختلف على نطاق واسع تبعا
على وقف الخسارة الخاص بك.

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

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

كود :

// External variables
extern bool DynamicLotSize = true;
extern double EquityPercent = 2;
extern double FixedLotSize = 0.1;

// Start function
if(DynamicLotSize == true)
{
double RiskAmount = AccountEquity() * (EquityPercent / 100);
double TickValue = MarketInfo(Symbol(),MODE_TICKVALUE);
if(Digits == 3 || Digits == 5) TickValue *= 10;
double CalcLots = (RiskAmount / StopLoss) / TickValue;
double LotSize = CalcLots;
}
else LotSize = FixedLotSize; 

إذا تم ضبط DynamicLotSize على true، فسنحسب حجم اللوت بناءً على وقف الخسارة ، ونحدد
قيمة متغير LotSize. إذا كانت DynamicLotSize تساوي false ، فنحن ببساطة نعيّن قيمة
FixedLotSize إلى LotSize. سيتم تمرير متغير LotSize إلى دالة OrderSend () مثل
حجم اللوت للطلب.

التحقق من حجم اللوت
Verifying Lot Size

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

دعنا نتحقق من الحد الأدنى والحد الأقصى لحجم اللوت أولاً. وظيفة MarketInfo () ، باستخدام
معلمات MODE_MINLOT و MODE_MAXLOT لمقارنة حجم العقد الحالي بـ
الحد الأدنى والحد الأقصى لحجم اللوت. إذا كان حجم اللوت غير صالح ، فسيتم تغيير حجمه تلقائيًا إلى
الحد الأدنى أو الحد الأقصى.

كود :

if(LotSize < MarketInfo(Symbol(),MODE_MINLOT))
{
LotSize = MarketInfo(Symbol(),MODE_MINLOT);
}
else if(LotSize > MarketInfo(Symbol(),MODE_MAXLOT))
{
LotSize = MarketInfo(Symbol(),MODE_MAXLOT);

نحن ببساطة نقارن قيمة LotSize ، حجم اللوت المحسوب أو الثابت من أعلى ، إلى الحد الأدنى
والحد الأقصى لحجم اللوت. إذا كان حجم اللوت أقل من الحد الأدنى لحجم اللوت ، أو أكبر من الحد الأقصى للحجم
، سيتم تعيين الحد الأدنى المناسب أو الحد الأقصى للقيمة المناسبة.

بعد ذلك ، نحتاج إلى مقارنة حجم اللوت لدينا بقيمة الخطوة. تشير قيمة الخطوة إلى ما إذا كان الوسيط أم لا
يسمح بعقود الميكرو (0.01) أو الميني لوت (0.1). إذا حاولت استخدام حجم لوت صغير على وسيط فقط
يسمح بالعقود الصغيرة ، سوف تحصل على خطأ ولن يتم وضع الصفقة. هذا هو الكود للتحقق من
قيمة الخطوة:

كود :

if(MarketInfo(Symbol(),MODE_LOTSTEP) == 0.1)
{
LotSize = NormalizeDouble(LotSize,1);
}
else LotSize = NormalizeDouble(LotSize,2 

);

تقرب الدالة NormalizeDouble () قيمة LotSize إلى عدد الأرقام المحددة في
الخانة الثانية. في السطر الأول ، إذا كان حجم الخطوة هو 0.1 ، فيشير ذلك إلى أن الوسيط يستخدم ميني لوت ، سيتم تقريب LotSize إلى منزلة عشرية واحدة. خلاف ذلك ، سيتم تقريب LotSize إلى 2 عشري
أماكن.

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

Other Considerations
سياق التجارة

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

ستعود الدالة IsTradeCon****Busy () بشكل صحيح إذا كان سياق تنفيذ التجارة مشغولاً ،
خلاف ذلك خطأ. سوف نستدعي هذه الوظيفة قبل استدعاء أي وظائف تداول ، بما في ذلك
OrderSend () أو OrderClose () أو OrderDelete () أو OrderModify ().

فيما يلي كيفية التحقق من سياق تنفيذ التداول باستخدام IsTradeCon****Busy ():

كود :

while(IsTradeCon****Busy()) Sleep(10);
int Ticket = OrderSend(Symbol(),OP_BUY,LotSize,Ask,UseSlippage, 0,0,
"Buy Order",MagicNumber,0,Green); 

نستخدم حلقة while لتقييم IsTradeCon****Busy (). إذا كانت الدالة ترجع true، فيشير إلى ذلك
سياق تنفيذ الصفقات مشغول ، سينام المستشار الخبير لمدة 10 مللي ثانية. في حين أن
الحلقة ستستمر في التنفيذ طالما أن IsTradeCon****Busy () ترجع true. وبمجرد ا تحرير السياق ، سيبدأ التداول.

إذا حاول المستشار الخبير التداول تنفيذ التجارة أثناء انشغال السياق ، فسيظهر الخطأ 147:
سينتج عنه “trade con**** busy”. على الرغم من أن هذه الطريقة يمكن الاعتماد عليها إلى حد ما في تجنب “سياق التجارة
مشغول “، فهي ليست مضمونًة ، خاصةً عندما يحاول العديد من المستشارين الخبراء التداول في
نفس الوقت. لاحقًا ان شاء الله، سوف نستكشف طرقًا لإعادة محاولة عمليات التداول بعد خطأ معين.

تحديث المتغيرات المحددة مسبقًا

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

تقوم الوظيفة *******Rates () بتحديث محتويات المتغيرات المحددة مسبقًا بأحدث الأسعار
من الخادم. يوصى باستدعاء هذه الوظيفة في كل مرة تستخدم فيها متغيرات العرض Bid أو الطلب Ask،
خاصة بعد تنفيذ صفقة سابقة.

لاحظ أنه إذا قمت باسترداد السعر باستخدام وظيفة MarketInfo () ، فليس من الضروري استخدام
*******Rates (). قمنا بتغطية MarketInfo () في دروس سابقة. عندما نصل إلى الفصل الخاص بإنشاء
الوظائف ، سنستخدم MarketInfo () لاسترداد الأسعار بدلاً من استخدام المتغيرات المحددة مسبقًا.
ومع ذلك ، قد لا تزال ترغب في استخدام العرض Bid و الطلب Ask في وظيفة start () للإشارة إلى الوضع الحالي
للأسعار على الرسم البياني.

Error Handling

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

نتحقق من الأخطاء المحتملة من خلال فحص مخرجات الوظائف مثل OrderSend () ،
OrderModify () و OrderClose (). إذا لم تكتمل الوظيفة بنجاح ، فستقوم الوظيفة
بإرجاع -1 لـ OrderSend () ، أو false لـ OrderModify () و OrderClose ().

في هذا الدرس ، سننشئ روتينا لمعالجة الأخطاء لوظيفة OrderSend (). إذا كانت
القيمة الراجعة OrderSend () هي -1 ، سنقوم بتشغيل روتين معالجة الأخطاء لعرض تنبيه للمستخدم ، و
تطبع معلمات التجارة ذات الصلة ومعلومات الأسعار في السجل (Log).

أولاً ، يجب علينا أولاً استرداد رمز الخطأ. يتم ذلك باستخدام دالة GetLastError (). نحن
بحاجة إلى تخزين القيمة المرجعة لـ GetLastError () في متغير ، لأنه بمجرد أن نستدعي GetLastError ()
بتم ارجاع رمز الخطأ وسيتم مسح قيمة الخطأ وسيتم ارجاع 0 للأستدعاء التالي لوظيفة GetLastError (). سنقوم
بتعريف متغير عام يسمى ErrorCode ونستخدمه لتخزين قيمة GetLastError ().

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

كود PHP:

#include stdlib.mqh 

أعلى ملفنا.

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

أخيرًا ، سنقوم بطباعة معلومات الأسعار ذات الصلة إلى السجل باستخدام وظيفة Print(). جنبا إلى جنب مع
أسعار العرض Bid والطلب Ask الحالية ، سنقوم بتضمين معلمات التجارة مثل حجم اللوت وسعر الأمر.

كود :

// Preprocessor section
#include <stdlib.mqh>
// Global variable
int ErrorCode;
// Order placement
int Ticket = OrderSend(Symbol(),OP_BUYSTOP,LotSize,PendingPrice ,UseSlippage,0,0,
"Buy Stop Order",MagicNumber,0,Green);
if(Ticket == -1)
{
ErrorCode = GetLastError();
string ErrDesc = ErrorDescription(ErrorCode);
string ErrAlert = StringConcatenate("Open Buy Stop Order - Error ",
ErrorCode,": ",ErrDesc);
Alert(ErrAlert);
string ErrLog = StringConcatenate("Bid: ",Bid," Ask: ",Ask," Price: ",
PendingPrice," Lots: ",LotSize);
Print(ErrLog);

في الجزء العلوي ، نقوم بتضمين ملف stdlib.mqh. نضيف المتغير العام ErrorCode لتخزين
شفرة الخطأ. يضع OrderSend () أمر إيقاف الشراء. إذا لم تنجح الوظيفة ، فإن معالجة الأخطاء لدينا
يتم تشغيل التعليمات البرمجية.

أولاً ، نقوم بتخزين قيمة GetLastError () في ErrorCode. ثم نستدعي دالة ErrorDescription ()
، باستخدام ErrorCode كوسيط. بعد ذلك ، نستخدم وظيفة StringConcatenate () لنقوم
بإنشاء رسالة التنبيه الخاصة بنا ، والتي يتم تخزينها في متغير السلسلة ErrAlertالنصية .

StringConcatenate () هي دالة MQL تسمح لك بإنشاء سلاسل معقدة باستخدام المتغيرات
والثوابت. كل عنصر سلسلة نصية يتم ربطه (أو “متسلسل”) معًا يتم فصله بواسطة
فاصلة.

يمكنك أيضًا ربط السلاسل النصية من خلال دمجها بعلامة الجمع (+). باستخدام
StringConcatenate () يكون أكثر وضوحًا وفعالية ، ولكن إذا كنت تريد ببساطة ربط سلسلة قصيرة
، استخدم علامة الجمع للجمع بين ثوابت السلسلة والمتغيرات:

string PlusCat = “The current Ask price is “+Ask;
// Sample output: The current Ask price is 1.4320

تعرض الوظيفة Alert () نافذة منبثقة على سطح مكتب المستخدم ، تحتوي على محتويات
متغير ال ErrAlert.

نقوم ببناء سلسلة أخرى بمعلمات السعر والتجارة الخاصة بنا ، ونخزنها في متغير ErrLog ،
التي نمررها إلى وظيفة Print() .
Print() تطبع محتويات وسيطة الدالة إلى سجل الخبراء.
يمكن الاطلاع على سجل الخبراء من علامة تبويب الخبراء داخل نافذة Terminal ، أو من
علامة التبويب Journal في نافذة Tester إذا كنت تستخدم Strategy Tester.

ها هي محتويات السجل. السطر الأول هو الناتج من وظيفة Alert(). السطر الثاني هو
إخراج وظيفة Print(). لاحظ الخطأ “حجم تداول غير صالح” “invalid trade volume”، وحقيقة أن
حجم اللوت المبلغ عنه في السجل هو 0. في هذه الحالة ، تكمن المشكلة في أن حجم اللوت غير صالح.

يمكنك إنشاء إجراءات معالجة أخطاء مماثلة لوظائف أخرى أيضًا ، خاصةً لـ
دالات OrderModify () و OrderClose (). يمكنك أيضًا إنشاء معالجة أكثر تعقيدًا لأخطاء
الإجراءات التي توفر رسائل خطأ مخصصة بناءً على رمز الخطأ ، أو تنفذ إجراءات أخرى.

على سبيل المثال ، إذا تلقيت الخطأ 130: “وقف غير صالح” “invalid stops” ، يمكنك عرض رسالة مثل سعر وقف الخسارة أو جني الأرباح غير صالح. ” “The stop
loss or take profit price is invalid.” إليك مثال على كيفية القيام بذلك:

كود :

ErrorCode = GetLastError();
string ErrDesc;
if(ErrorCode == 129) ErrDesc = "Order opening price is invalid!";
if(ErrorCode == 130) ErrDesc = "Stop loss or take profit is invalid!";
if(ErrorCode == 131) ErrDesc = "Lot size is invalid!";
string ErrAlert = StringConcatenate("Open Buy Order - Error ",ErrorCode,": ",ErrDesc);
Alert(ErrAlert); 

Putting It All Together

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

كود :

#property copyright "Mohammad al turk"
#include <stdlib.mqh>
// External variables
extern bool DynamicLotSize = true;
extern double EquityPercent = 2;
extern double FixedLotSize = 0.1;
extern double StopLoss = 50;
extern double TakeProfit = 100;
extern int Slippage = 5;
extern int MagicNumber = 123;
extern int FastMAPeriod = 10;
extern int SlowMAPeriod = 20;
// Global variables
int BuyTicket;
int SellTicket;
double UsePoint;
int UseSlippage;
int ErrorCode; 

لقد أضفنا العبارة #include لملف stdlib.mqh الذي يحتوي على الامتداد
ErrorDescription () وظيفة لإجراءات معالجة الأخطاء لدينا. أضفنا ثلاثة متغيرات خارجية
لحجم اللوت ، ومتغير عام لرمز الخطأ.

يتم وضع الكود التالي في بداية وظيفة start ():

كود :

// Moving averages
double FastMA = iMA(NULL,0,FastMAPeriod,0,0,0,0);
double SlowMA = iMA(NULL,0,SlowMAPeriod,0,0,0,0);
// Lot size calculation
if(DynamicLotSize == true)
{
double RiskAmount = AccountEquity() * (EquityPercent / 100);
double TickValue = MarketInfo(Symbol(),MODE_TICKVALUE);
if(Point == 0.001 || Point == 0.00001) TickValue *= 10;
double CalcLots = (RiskAmount / StopLoss) / TickValue;
double LotSize = CalcLots;
}
else LotSize = FixedLotSize;
// Lot size verification
if(LotSize < MarketInfo(Symbol(),MODE_MINLOT))
{
LotSize = MarketInfo(Symbol(),MODE_MINLOT);
}
else if(LotSize > MarketInfo(Symbol(),MODE_MAXLOT))
{
LotSize = MarketInfo(Symbol(),MODE_MAXLOT);
}
if(MarketInfo(Symbol(),MODE_LOTSTEP) == 0.1)
{
LotSize = NormalizeDouble(LotSize,1);
}
else LotSize = NormalizeDouble(LotSize,2); 

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

كود :

// Buy Order
if(FastMA > SlowMA && BuyTicket == 0)
{
// Close Order
OrderSelect(SellTicket,SELECT_BY_TICKET);
if(OrderCloseTime() == 0 && SellTicket > 0)
{
double CloseLots = OrderLots();
while(IsTradeCon****Busy()) Sleep(10);
*******Rates();
double ClosePrice = Ask;
bool Closed = OrderClose(SellTicket,CloseLots,ClosePrice,UseSlip page,Red);
// Error handling
if(Closed == false)
{
ErrorCode = GetLastError();
string ErrDesc = ErrorDescription(ErrorCode);
string ErrAlert = StringConcatenate("Close Sell Order - Error ",
ErrorCode,": ",ErrDesc);
Alert(ErrAlert);
string ErrLog = StringConcatenate("Ask: ",Ask," Lots: ",LotSize,
" Ticket: ",SellTicket);
Print(ErrLog);
}
}

// Open buy order
while(IsTradeCon****Busy()) Sleep(10);
*******Rates();
BuyTicket = OrderSend(Symbol(),OP_BUY,LotSize,Ask,UseSlippage, 0,0,
"Buy Order",MagicNumber,0,Green);
// Error handling
if(BuyTicket == -1)
{
ErrorCode = GetLastError();
ErrDesc = ErrorDescription(ErrorCode);
ErrAlert = StringConcatenate("Open Buy Order - Error ",
ErrorCode,": ",ErrDesc);
Alert(ErrAlert);
ErrLog = StringConcatenate("Ask: ",Ask," Lots: ",LotSize);
Print(ErrLog);
}
// Order modification
else
{
OrderSelect(BuyTicket,SELECT_BY_TICKET);
double OpenPrice = OrderOpenPrice();
// Calculate stop level
double StopLevel = MarketInfo(Symbol(),MODE_STOPLEVEL) * Point;
*******Rates();
double UpperStopLevel = Ask + StopLevel;
double LowerStopLevel = Bid - StopLevel;
double MinStop = 5 * UsePoint;

// Calculate stop loss and take profit
if(StopLoss > 0) double BuyStopLoss = OpenPrice - (StopLoss * UsePoint);
if(TakeProfit > 0) double BuyTakeProfit = OpenPrice + (TakeProfit * UsePoint);
// Verify stop loss and take profit
if(BuyStopLoss > 0 && BuyStopLoss > LowerStopLevel)
{
BuyStopLoss = LowerStopLevel - MinStop;
}
if(BuyTakeProfit > 0 && BuyTakeProfit < UpperStopLevel)
{
BuyTakeProfit = UpperStopLevel + MinStop;
}
// Modify order
if(IsTradeCon****Busy()) Sleep(10);
if(BuyStopLoss > 0 || BuyTakeProfit > 0)
{
bool TicketMod = OrderModify(BuyTicket,OpenPrice,BuyStopLoss,
BuyTakeProfit,0);
// Error handling
if(TicketMod == false)
{
ErrorCode = GetLastError();
ErrDesc = ErrorDescription(ErrorCode);
ErrAlert = StringConcatenate("Modify Buy Order - Error ",
ErrorCode,": ",ErrDesc);
Alert(ErrAlert);
ErrLog = StringConcatenate("Ask: ",Ask," Bid: ",Bid," Ticket: ",
BuyTicket," Stop: ",BuyStopLoss," Profit: ",BuyTakeProfit);
Print(ErrLog);
}
}
}
SellTicket = 0;

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

لاحظ أننا أضفنا وظيفة IsTradeCon****Busy () قبل كل عملية تداول. نحن نريد
التأكد من ان سياق التجارة غير مشغول قبل محاولة التداول. نحن نستخدم وظيفة *******Rates ()
قبل كل مرجع لمتغيرات العطاء Bid أو الطلب Ask ، للتأكد من أننا نستخدم دائمًا
أحدث الأسعار.

نبدأ بتحديد تذكرة أمر البيع السابقة وإغلاقها باستخدام OrderClose (). فإذا
فشلت الوظيفة ، يتم تشغيل كتلة معالجة الخطأ. بعد ذلك ، نفتح أمر شراء السوق باستخدام
OrderSend (). إذا فشلت الوظيفة ، يتم تشغيل كتلة معالجة الخطأ. خلاف ذلك ، نواصل إلى
كتلة تعديل الطلب.

نختار تذكرة الأمر الذي تم تقديمه للتو باستخدام OrderSelect () ، ونحدد سعر فتح الأمر
إلى متغير OpenPrice. ثم نحسب مستوى الإيقاف ومستوى الإيقاف العلوي والسفلي
للأسعار. بعد ذلك ، نحسب أسعار وقف الخسارة وجني الأرباح ، ونتحقق منها ، وأخيرًا نقوم بتعديل
الطلب باستخدام OrderModify (). كتلة معالجة الخطأ النهائية تتعامل مع الأخطاء من
تعديل الطلب.

إليك كيفية تعديل كود أمر إيقاف الشراء المعلق:

كود :





لقد أضفنا الكود لحذف الطلبات المعلقة باستخدام OrderDelete () بعد وظيفة OrderClose ()
يحدد نوع أمر البيع السابق الوظيفة المستخدمة لإغلاق الطلب.

يتمثل الاختلاف الرئيسي بين الكود التالي وكود أمر السوق في أنه ليس لدينا
كتلة تعديل الطلب. ليس من الضروري وضع وقف الخسارة وجني الأرباح بشكل منفصل
للأوامر المعلقة. لذلك سنقوم بحساب وقف الخسارة وجني الأرباح قبل وضع الأمر مع
OrderSend ().

كود :

// Calculate stop level
double StopLevel = MarketInfo(Symbol(),MODE_STOPLEVEL) * Point;
*******Rates();
double UpperStopLevel = Ask + StopLevel;
double MinStop = 5 * UsePoint;
// Calculate pending price
double PendingPrice = High[0] + (PendingPips * UsePoint);
if(PendingPrice < UpperStopLevel) PendingPrice = UpperStopLevel + MinStop;
// Calculate stop loss and take profit
if(StopLoss > 0) double BuyStopLoss = PendingPrice - (StopLoss * UsePoint);
if(TakeProfit > 0) double BuyTakeProfit = PendingPrice + (TakeProfit * UsePoint);
// Verify stop loss and take profit
UpperStopLevel = PendingPrice + StopLevel;
double LowerStopLevel = PendingPrice – StopLevel;
if(BuyStopLoss > 0 && BuyStopLoss > LowerStopLevel)
{
BuyStopLoss = LowerStopLevel - MinStop;
}
if(BuyTakeProfit > 0 && BuyTakeProfit < UpperStopLevel)
{
BuyTakeProfit = UpperStopLevel + MinStop;
}
// Place pending order
if(IsTradeCon****Busy()) Sleep(10);
BuyTicket = OrderSend(Symbol(),OP_BUYSTOP,LotSize,PendingPrice ,UseSlippage,
BuyStopLoss,BuyTakeProfit,"Buy Stop Order",MagicNumber,0,Green);
// Error handling
if(BuyTicket == -1)
{
ErrorCode = GetLastError();
ErrDesc = ErrorDescription(ErrorCode);
ErrAlert = StringConcatenate("Open Buy Stop Order - Error ",ErrorCode,
": ",ErrDesc);
Alert(ErrAlert);
ErrLog = StringConcatenate("Ask: ",Ask," Lots: ",LotSize," Price: ",PendingPrice,
" Stop: ",BuyStopLoss," Profit: ",BuyTakeProfit);
Print(ErrLog);
}
SellTicket = 0; 

أولاً ، نحسب مستوى التوقف العلوي. ثم نحسب ونتحقق من سعر الأمر المعلق لدينا ، والذي
يتم تخزينه في PendingPrice. ثم نعيد حساب UpperStopLevel ونحسب LowerStopLevel
بحيث تكون مرتبطة بسعر الأمر المعلق. لاحظ أننا لسنا بحاجة إلى استخدام العرض Bid أو الطلب Ask
الأسعار ، أو الرقم في السبريد عند التحقق من أسعار وقف الخسارة وجني الأرباح.

أخيرًا ، نضع أمرنا المعلق باستخدام OrderSend () ، مع وضع وقف الخسارة وجني الأرباح
معها. لدينا وظيفة معالجة الأخطاء القياسية للتعامل مع أخطاء وضع الطلبات.

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

الكود الكامل لهذا الخبير المستشار

كود :

#property copyright "Mohammad al turk"
#include <stdlib.mqh>
// External variables
extern bool DynamicLotSize = true;
extern double EquityPercent = 2;
extern double FixedLotSize = 0.1;
extern double StopLoss = 50;
extern double TakeProfit = 100;
extern int Slippage = 5;
extern int MagicNumber = 123;
extern int FastMAPeriod = 10;
extern int SlowMAPeriod = 20;
// Global variables
int BuyTicket;
int SellTicket;
double UsePoint;
int UseSlippage;
int ErrorCode;
// Init function
int init()
{
UsePoint = PipPoint(Symbol());
UseSlippage = GetSlippage(Symbol(),Slippage);
}
// Start function
int start()
{
// Moving averages
double FastMA = iMA(NULL,0,FastMAPeriod,0,0,0,1);
double SlowMA = iMA(NULL,0,SlowMAPeriod,0,0,0,1);
/ Lot size calculation
if(DynamicLotSize == true)
{
double RiskAmount = AccountEquity() * (EquityPercent / 100);
double TickValue = MarketInfo(Symbol(),MODE_TICKVALUE);
if(Point == 0.001 || Point == 0.00001) TickValue *= 10;
double CalcLots = (RiskAmount / StopLoss) / TickValue;
double LotSize = CalcLots;
}
else LotSize = FixedLotSize;
// Lot size verification
if(LotSize < MarketInfo(Symbol(),MODE_MINLOT))
{
LotSize = MarketInfo(Symbol(),MODE_MINLOT);
}
else if(LotSize > MarketInfo(Symbol(),MODE_MAXLOT))
{
LotSize = MarketInfo(Symbol(),MODE_MAXLOT);
}
if(MarketInfo(Symbol(),MODE_LOTSTEP) == 0.1)
{
LotSize = NormalizeDouble(LotSize,1);
}
else LotSize = NormalizeDouble(LotSize,2);
// Buy Order
if(FastMA > SlowMA && BuyTicket == 0)
{
// Close Order
OrderSelect(SellTicket,SELECT_BY_TICKET);
if(OrderCloseTime() == 0 && SellTicket > 0)
{
double CloseLots = OrderLots();
while(IsTradeCon****Busy()) Sleep(10);
*******Rates();
double ClosePrice = Ask;
bool Closed = OrderClose(SellTicket,CloseLots,ClosePrice,UseSlip page,Red);
// Error handling
if(Closed == false)
{
ErrorCode = GetLastError();
string ErrDesc = ErrorDescription(ErrorCode);
string ErrAlert = StringConcatenate("Close Sell Order - Error ",
ErrorCode,": ",ErrDesc);
Alert(ErrAlert);
string ErrLog = StringConcatenate("Ask: ",Ask," Lots: ",LotSize,
" Ticket: ",SellTicket);
Print(ErrLog);
}
}
// Open buy order
while(IsTradeCon****Busy()) Sleep(10);
*******Rates();
BuyTicket = OrderSend(Symbol(),OP_BUY,LotSize,Ask,UseSlippage, 0,0,
"Buy Order",MagicNumber,0,Green);
// Error handling
if(BuyTicket == -1)
{
ErrorCode = GetLastError();
ErrDesc = ErrorDescription(ErrorCode);
ErrAlert = StringConcatenate("Open Buy Order - Error ",ErrorCode,
": ",ErrDesc);
Alert(ErrAlert);
ErrLog = StringConcatenate("Ask: ",Ask," Lots: ",LotSize);
Print(ErrLog);
}
// Order modification
else
{
OrderSelect(BuyTicket,SELECT_BY_TICKET);
double OpenPrice = OrderOpenPrice();
// Calculate stop level
double StopLevel = MarketInfo(Symbol(),MODE_STOPLEVEL) * Point;
*******Rates();
double UpperStopLevel = Ask + StopLevel;
double LowerStopLevel = Bid - StopLevel;
double MinStop = 5 * UsePoint;
// Calculate stop loss and take profit
if(StopLoss > 0) double BuyStopLoss = OpenPrice - (StopLoss * UsePoint);
if(TakeProfit > 0) double BuyTakeProfit = OpenPrice +
(TakeProfit * UsePoint);
// Verify stop loss and take profit
if(BuyStopLoss > 0 && BuyStopLoss > LowerStopLevel)
{
BuyStopLoss = LowerStopLevel - MinStop;
}
if(BuyTakeProfit > 0 && BuyTakeProfit < UpperStopLevel)
{
BuyTakeProfit = UpperStopLevel + MinStop;
}
// Modify order
if(IsTradeCon****Busy()) Sleep(10);
if(BuyStopLoss > 0 || BuyTakeProfit > 0)
{
bool TicketMod = OrderModify(BuyTicket,OpenPrice,BuyStopLoss,
BuyTakeProfit,0);
// Error handling
if(TicketMod == false)
{
ErrorCode = GetLastError();
ErrDesc = ErrorDescription(ErrorCode);
ErrAlert = StringConcatenate("Modify Buy Order - Error ",ErrorCode,
": ",ErrDesc);
Alert(ErrAlert);
ErrLog = StringConcatenate("Ask: ",Ask," Bid: ",Bid," Ticket: ",
BuyTicket," Stop: ",BuyStopLoss," Profit: ",BuyTakeProfit);
Print(ErrLog);
}
}
}
SellTicket = 0;
}
// Sell Order
if(FastMA < SlowMA && SellTicket == 0)
{
OrderSelect(BuyTicket,SELECT_BY_TICKET);
if(OrderCloseTime() == 0 && BuyTicket > 0)
{
CloseLots = OrderLots();
while(IsTradeCon****Busy()) Sleep(10);
*******Rates();
ClosePrice = Bid;
Closed = OrderClose(BuyTicket,CloseLots,ClosePrice,UseSlipp age,Red);
// Error handling
if(Closed == false)
{
ErrorCode = GetLastError();
ErrDesc = ErrorDescription(ErrorCode);
ErrAlert = StringConcatenate("Close Buy Order - Error ",ErrorCode,
": ",ErrDesc);
Alert(ErrAlert);
ErrLog = StringConcatenate("Bid: ",Bid," Lots: ",LotSize," Ticket: ",
BuyTicket);
Print(ErrLog);
}
}
while(IsTradeCon****Busy()) Sleep(10);
*******Rates();
SellTicket = OrderSend(Symbol(),OP_SELL,LotSize,Bid,UseSlippage ,0,0,
"Sell Order", MagicNumber,0,Red);
// Error handling
if(SellTicket == -1)
{
ErrorCode = GetLastError();
ErrDesc = ErrorDescription(ErrorCode);
ErrAlert = StringConcatenate("Open Sell Order - Error ",ErrorCode,
": ",ErrDesc);
Alert(ErrAlert);
ErrLog = StringConcatenate("Bid: ",Bid," Lots: ",LotSize);
Print(ErrLog);
}
else
{
OrderSelect(SellTicket,SELECT_BY_TICKET);
OpenPrice = OrderOpenPrice();
StopLevel = MarketInfo(Symbol(),MODE_STOPLEVEL) * Point;
*******Rates();
UpperStopLevel = Ask + StopLevel;
LowerStopLevel = Bid - StopLevel;
MinStop = 5 * UsePoint;
if(StopLoss > 0) double SellStopLoss = OpenPrice + (StopLoss * UsePoint);
if(TakeProfit > 0) double SellTakeProfit = OpenPrice -
(TakeProfit * UsePoint);
if(SellStopLoss > 0 && SellStopLoss < UpperStopLevel)
{
SellStopLoss = UpperStopLevel + MinStop;
}
if(SellTakeProfit > 0 && SellTakeProfit > LowerStopLevel)
{
SellTakeProfit = LowerStopLevel - MinStop;
}
if(IsTradeCon****Busy()) Sleep(10);
if(SellStopLoss > 0 || SellTakeProfit > 0)
{
TicketMod = OrderModify(SellTicket,OpenPrice,SellStopLoss,
SellTakeProfit,0);
// Error handling
if(TicketMod == false)
{
ErrorCode = GetLastError();
ErrDesc = ErrorDescription(ErrorCode);
ErrAlert = StringConcatenate("Modify Sell Order - Error ",
ErrorCode,": ",ErrDesc);
Alert(ErrAlert);
ErrLog = StringConcatenate("Ask: ",Ask," Bid: ",Bid," Ticket: ",
SellTicket," Stop: ",SellStopLoss," Profit: ",SellTakeProfit);
Print(ErrLog);
}
}
}
BuyTicket = 0;
}
return(0);
}
// Pip Point Function
double PipPoint(string Currency)
{
int CalcDigits = MarketInfo(Currency,MODE_DIGITS);
if(CalcDigits == 2 || CalcDigits == 3) double CalcPoint = 0.01;
else if(CalcDigits == 4 || CalcDigits == 5) CalcPoint = 0.0001;
return(CalcPoint);
}
// Get Slippage Function
int GetSlippage(string Currency, int SlippagePips)
{
int CalcDigits = MarketInfo(Currency,MODE_DIGITS);
if(CalcDigits == 2 || CalcDigits == 4) double CalcSlippage = SlippagePips;
else if(CalcDigits == 3 || CalcDigits == 5) CalcSlippage = SlippagePips * 10;
return(CalcSlippage);

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

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

للحفاظ على التسلسل، سنحافظ على نفس الأسماء لأي متغيرات خارجية استخدمناها حتى الآن. سنسبق هذه المتغيرات بـ “arg” للإشارة إلى أنها وسائط دالة

دالة تحديد حجم اللوت

لنبدأ باحتساب حجم اللوت الخاص بنا.

كود :

double CalcLotSize(bool argDynamicLotSize, double argEquityPercent, double argStopLoss,
double argFixedLotSize)
{
if(argDynamicLotSize == true)
{
double RiskAmount = AccountEquity() * (argEquityPercent / 100);
double TickValue = MarketInfo(Symbol(),MODE_TICKVALUE);
if(Point == 0.001 || Point == 0.00001) TickValue *= 10;
double LotSize = (RiskAmount / argStopLoss) / TickValue;
}
else LotSize = argFixedLotSize;
return(LotSize);

السطر الأول هو إعلان الدالة. نسمي هذه الدالة “CalcLotSize()”. لاحظ أن “DynamicLotSize”، “EquityPercent”، “StopLoss” و “FixedLotSize” أصبحت الآن وسائط دالة. لا تزال المتغيرات الخارجية بهذه الأسماء موجودة في برنامجنا، ولكننا سنمررها الآن إلى الدالة كوسائط.

تمامًا مع شفرة حساب حجم اللوت السابقة. لقد أضفنا عبارة return في نهاية الدالة – هذا سيعيد قيمة حجم اللوت إلى الدالة التي تم استدعاؤها.

ستوضع الدالة نفسها في مكان ما في ملف البرنامج الخاص بنا، خارج الدوال start() و init()، أو سيتم وضعها في ملف include خارجي. في الحالة الأخيرة، سيتضمن عبارة #include في بداية البرنامج الملف للاستخدام في البرنامج.

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

كود :

extern bool DynamicLotSize = true;
extern double EquityPercent = 2;
extern double FixedLotSize = 0.1;
extern double StopLoss = 50; 

وهذه هي الطريقة التي نسمي بها الوظيفة. سيكون هذا السطر من التعليمات البرمجية موجودًا داخل وظيفة start ():

كود :

double LotSize = CalcLotSize(DynamicStopLoss,EquityPercent,StopLoss ,FixedLotSize); 

يتم تمرير المتغيرات الخارجية لدينا إلى الدالة كوسائط. ستحسب الدالة حجم اللوت الخاص بنا، وسيتم حفظ القيمة في المتغير LotSize. لاحظ أن هذا المتغير يختلف عن المتغير LotSize الذي يوجد داخل دالة CalcLotSize(). كلا المتغيرين محليان لوظائفهما، لذلك على الرغم من وجود نفس الاسم، إلا أنهما ليسا نفس المتغير.

دالة التحقق من حجم اللوت

دعنا نستمر في كود التحقق من حجم اللوت الموجود في الصفحة 51. ستكون هذه دالة منفصلة، في حال قررت استخدام طريقة بديلة لحساب حجم اللوت. بغض النظر عن طريقة تحديد حجم اللوت، ستريد التحقق منه قبل استخدامه وتمريره إلى دالة وضع الأمر:

كود :

double VerifyLotSize(double argLotSize)
{
if(argLotSize < MarketInfo(Symbol(),MODE_MINLOT))
{
argLotSize = MarketInfo(Symbol(),MODE_MINLOT);
}
else if(argLotSize > MarketInfo(Symbol(),MODE_MAXLOT))
{
argLotSize = MarketInfo(Symbol(),MODE_MAXLOT);
}
if(MarketInfo(Symbol(),MODE_LOTSTEP) == 0.1)
{
argLotSize = NormalizeDouble(argLotSize,1);
}
else argLotSize = NormalizeDouble(argLotSize,2);
return(argLotSize);

لهذه الدالة، سنمرر المتغير الذي يحمل حجم اللوت الذي حسبناه باستخدام دالة CalcLotSize() كوسيط. سيتم معالجة المتغير الموجود في الوسيط argLotSize ثم إرجاع النتيجة إلى الدالة المستدعية.

وظيفة ترتيب الطلبات

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

سنضع أمر الشراء بالسعر الحالي باستخدام OrderSend() ، وإذا لم يتم وضع الطلب ، فسنقوم بتشغيل شفرة معالجة الأخطاء السابقة. سنقوم بإرجاع رقم التذكرة إلى الدالة المستدعاة ، أو -1 إذا لم يتم وضع الطلب.

نحدد رمز الطلب باستخدام متغير argSymbol كوسيط ، بدلاً من استخدام رمز الرسم البياني الحالي. بهذه الطريقة ، إذا قررت وضع طلب على رمز آخر ، يمكنك القيام بذلك بسهولة. بدلاً من استخدام المتغيرات المحددة مسبقًا Bid و Ask ، سنحتاج إلى استخدام وظيفة MarketInfo() مع معلمات MODE_ASK و MODE_BID لاسترداد سعر العرض والطلب لذلك الرمز الخاص.

المفترض أن يكون حجم اللوت والانزلاق قد تم حسابهما وتأكيدهما قبل استدعاء هذه الوظيفة. كما أننا قمنا بتحديد قيمة افتراضية لتعليق الأمر. المتغير argComment يحتوي على القيمة الافتراضية “Buy Order”. إذا لم يتم تحديد قيمة لهذا الوسيط ، فسيتم استخدام القيمة الافتراضية.

كود :

int OpenBuyOrder(string argSymbol, double argLotSize, double argSlippage,
double argMagicNumber, string argComment = "Buy Order")
{
while(IsTradeCon****Busy()) Sleep(10);
// Place Buy Order
int Ticket = OrderSend(argSymbol,OP_BUY,argLotSize,MarketInfo(a rgSymbol,MODE_ASK),
argSlippage,0,0,argComment,argMagicNumber,0,Green) ;
// Error Handling
if(Ticket == -1)
{
int ErrorCode = GetLastError();
string ErrDesc = ErrorDescription(ErrorCode);
string ErrAlert = StringConcatenate("Open Buy Order – Error ",
ErrorCode,": ",ErrDesc);
Alert(ErrAlert);
string ErrLog = StringConcatenate("Bid: ",MarketInfo(argSymbol,MODE_BID),
" Ask: ",MarketInfo(argSymbol,MODE_ASK)," Lots: ",argLotSize);
Print(ErrLog);
}
return(Ticket);

في دالة OrderSend() ، لاحظ أننا استخدمنا دالة MarketInfo() بمعامل MODE_ASK بدلاً من المتغير المحدد مسبقًا Ask. سيتم بذلك استرداد سعر ASK الحالي للعملة المحددة بواسطة argSymbol.

إذا لم يتم وضع التداول بنجاح ، سيتم تشغيل روتين إدارة الأخطاء. وإلا ، سيتم إرجاع تذكرة الطلب إلى الدالة المستدعية ، أو -1 إذا لم يتم وضع الطلب.

Pending Order Placement

لوضع أوامر معلقة سنحتاج إلى تمرير معاملات لسعر الأمر المعلق وكذلك وقت انتهاء الأمر. سيتم إضافة معاملات argPendingPrice و argExpiration إلى الدالة.

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

هذا هو الكود اللازم لوضع أمر شراء معلق “buy stop”:

كود :

int OpenBuyStopOrder(string argSymbol, double argLotSize, double argPendingPrice,
double argStopLoss, double argTakeProfit, double argSlippage, double argMagicNumber,
datetime argExpiration = 0, string argComment = "Buy Stop Order")
{
while(IsTradeCon****Busy()) Sleep(10);
// Place Buy Stop Order
int Ticket = OrderSend(argSymbol,OP_BUYSTOP,argLotSize,argPendi ngPrice,
argSlippage,argStopLoss,argTakeProfit,argComment,a rgMagicNumber,
argExpiration,Green);
// Error Handling
if(Ticket == -1)
{
int ErrorCode = GetLastError();
string ErrDesc = ErrorDescription(ErrorCode);
string ErrAlert = StringConcatenate("Open Buy Stop Order - Error ",ErrorCode,
": ",ErrDesc);
Alert(ErrAlert);
string ErrLog = StringConcatenate("Ask: ",MarketInfo(argSymbol,MODE_ASK),
" Lots: ",argLotSize," Price: ",argPendingPrice," Stop: ",argStopLoss,
" Profit: ",argTakeProfit," Expiration: ",TimeToStr(argExpiration));
Print(ErrLog);
}
return(Ticket);

يرجى ملاحظة أننا حددنا قيمة افتراضية مساوية لـ 0 للمعاملة argExpiration. إذا كنت لا تستخدم وقت انتهاء الأمر المعلق، وترغب في استخدام التعليق الافتراضي للأمر، فيمكنك ببساطة حذف المعاملات argExpiration و argComment عند استدعاء الدالة. وفيما يلي مثال يوضح كيفية وضع أمر شراء معلق “buy stop” بدون وقت انتهاء وبالتعليق الافتراضي “Buy Stop Order”:

كود :

int Ticket = OpenBuyStopOrder(Symbol(),LotSize,PendingPrice,Sto pLoss,TakeProfit,
UseSlippage,MagicNumber); 

لقد أضفنا سعر الأمر المعلق إلى السجل في وظيفة معالجة الأخطاء، وكذلك وقت الانتهاء، إذا تم تحديده. تقوم دالة TimeToStr() بتحويل المتغير datetime إلى تنسيق سلسلة قراءة.

تعمل الوظائف المستخدمة لفتح أوامر بيع معلقة، وشراء بحدود، وبيع بحدود بنفس الطريقة التي تم شرحها هنا، والفرق الوحيد هو أن معامل نوع الأمر لدالة OrderSend() يتم تغييره بالتوافق مع نوع الأمر المطلوب.

وظيفة إغلاق الأمر
Order Closing Function

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

كود :

bool CloseBuyOrder(string argSymbol, int argCloseTicket, double argSlippage)
{
OrderSelect(argCloseTicket,SELECT_BY_TICKET);
if(OrderCloseTime() == 0)
{
double CloseLots = OrderLots();
while(IsTradeCon****Busy()) Sleep(10);
double ClosePrice = MarketInfo(argSymbol,MODE_ASK);
bool Closed = OrderClose(argCloseTicket,CloseLots,ClosePrice,arg Slippage,Red);
if(Closed == false)
{
int ErrorCode = GetLastError();
string ErrDesc = ErrorDescription(ErrorCode);
string ErrAlert = StringConcatenate("Close Buy Order - Error: ",ErrorCode,
": ",ErrDesc);
Alert(ErrAlert);
string ErrLog = StringConcatenate("Ticket: ",argCloseTicket," Ask: ",
MarketInfo(argSymbol,MODE_ASK));
Print(ErrLog);
}
}
return(Closed);

بالنسبة لمتغير ClosePrice، نستخدم MarketInfo() لاسترداد سعر العرض الحالي للعملة المحددة بواسطة argSymbol. نستخدم وسيط CloseTicket و Slippage الخاص بالدالة لتنفيذ أمر الإغلاق والانزلاق على التوالي. إذا لم يتم إغلاق الأمر بنجاح، فإننا نقوم بتشغيل كتلة معالجة الأخطاء، التي تقوم بطباعة رقم التذكرة وسعر العرض الحالي في السجل.

الكود الخاص بإغلاق أمر بيع سيكون متطابقًا، باستثناء استخدام سعر العرض لمتغير ClosePrice.

Pending Order Close Function

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

كود :

bool ClosePendingOrder(string argSymbol, int argCloseTicket, double argSlippage)
{
OrderSelect(argCloseTicket,SELECT_BY_TICKET);
if(OrderCloseTime() == 0)
{
while(IsTradeCon****Busy()) Sleep(10);
bool Deleted = OrderDelete(argCloseTicket,Red);
if(Deleted == false)
{
int ErrorCode = GetLastError();
string ErrDesc = ErrorDescription(ErrorCode);
string ErrAlert = StringConcatenate("Close Pending Order - Error: ",
ErrorCode,": ",ErrDesc);
Alert(ErrAlert);
string ErrLog = StringConcatenate("Ticket: ",argCloseTicket,
" Bid: ",MarketInfo(argSymbol,MODE_BID),
" Ask: ",MarketInfo(argSymbol,MODE_ASK));
Print(ErrLog);
}
}
return(Deleted);

وظائف حساب وقف الخسارة وجني الأرباح.
Stop Loss & Take Profit Calculation Functions

سوف نقوم بإنشاء وظائف قصيرة لحساب وقف الخسارة وجني الأرباح. سنقوم بتمرير المتغيرات الخارجية التي تشير إلى وقف الخسارة أو جني الأرباح في نقاط على وظيفتنا، بالإضافة إلى سعر فتح الأمر. قيمة إرجاع وظيفتنا ستكون سعر وقف الخسارة أو جني الأرباح الفعلي.

وإليكم وظيفة لحساب وقف الخسارة عند الشراء بالنقاط

كود :

double CalcBuyStopLoss(string argSymbol, int argStopLoss, double argOpenPrice)
{
if(argStopLoss == 0) return(0);
double BuyStopLoss = argOpenPrice - (argStopLoss * PipPoint(argSymbol));
return(BuyStopLoss);

أولاً، سنتحقق مما إذا كان مستوى وقف الخسارة الصالح تم تمريره مع الوظيفة. إذا كان متغير “argStopLoss” يساوي 0، فسوف نرجع قيمة 0 إلى الدالة المستدعاة، مشيرين إلى أنه لم يتم تحديد أي وقف خسارة.

ثم، سنقوم بحساب وقف الخسارة عن طريق طرح قيمة وقف الخسارة المعبرة بالنقاط من سعر فتح الأمر. وسنقوم بضرب متغير “argStopLoss” بقيمة نقطة الفرق (Pip Point) لحساب القيمة العشرية، ونطرح ذلك من “argOpenPrice”. وسنستخدم سواء سعر العرض أو الطلب (للأوامر السوقية) أو سعر الأمر المعلق المقصود.

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

وإليكم الوظيفة لحساب جني الأرباح عند الشراء بالنقاط:

كود :

double CalcBuyTakeProfit(string argSymbol, int argTakeProfit, double argOpenPrice)
{
if(argTakeProfit == 0) return(0);
double BuyTakeProfit = OpenPrice + (argTakeProfit * PipPoint(argSymbol));
return(BuyTakeProfit);

يرجى ملاحظة أن الوظيفة المستخدمة لحساب وقف الخسارة عند البيع تقريبًا مطابقة لتلك المستخدمة لحساب جني الأرباح عند الشراء، والعكس صحيح لحساب وقف الخسارة عند الشراء وجني الأرباح عند البيع

التحقق من مستوى وقف الخسارة.
Stop Level Verification

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

تقوم الوظيفة التالية بالتحقق مما إذا كان السعر أعلى من مستوى وقف الخسارة العلوي (سعر فتح الأمر + مستوى وقف الخسارة). إذا كان السعر أعلى من ذلك، فإن الوظيفة تعيد القيمة صحيح (true)، وإلا فإنها تعيد القيمة خطأ (false):

كود :

bool VerifyUpperStopLevel(string argSymbol, double argVerifyPrice,
double argOpenPrice = 0)
{
double StopLevel = MarketInfo(argSymbol,MODE_STOPLEVEL) * Point;
if(argOpenPrice == 0) double OpenPrice = MarketInfo(argSymbol,MODE_ASK);
else OpenPrice = argOpenPrice;
double UpperStopLevel = OpenPrice + StopLevel;
if(argVerifyPrice > UpperStopLevel) bool StopVerify = true;
else StopVerify = false;
return(StopVerify);

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

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

كود :

bool Verified = VerifyUpperStopLevel(Symbol(),SellStopLoss);
if(Verified == false) Alert("Sell stop loss is invalid!"); 

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

الدالة التالية تحقق ما إذا كان السعر فوق مستوى الإيقاف العلوي (سعر فتح الأمر بالإضافة إلى مستوى الإيقاف). إذا كان الأمر كذلك، فإن الدالة تعيد قيمة صحيحة، وإلا فهي تعيد قيمة خاطئة:

كود :

double AdjustAboveStopLevel(string argSymbol, double argAdjustPrice, int argAddPips = 0,
double argOpenPrice = 0)
{
double StopLevel = MarketInfo(argSymbol,MODE_STOPLEVEL) * Point;
if(argOpenPrice == 0) double OpenPrice = MarketInfo(argSymbol,MODE_ASK);
else OpenPrice = argOpenPrice;
double UpperStopLevel = OpenPrice + StopLevel;
if(argAdjustPrice <= UpperStopLevel)
{
double AdjustedPrice = UpperStopLevel + (argAddPips * PipPoint(argSymbol));
}
else AdjustedPrice = argAdjustPrice;
return(AdjustedPrice);

يتم تمرير وسيطات إلى هذه الوظيفة ، حيث يتم التحقق منها وتعديلها إذا كانت غير صالحة. يتم استخدام وسيطة argAdjustPrice للتحقق والتعديل (إذا لزم الأمر) ، بينما يتم استخدام وسيطة argAddPips لإضافة عدد محدد من النقاط عند ضبط السعر. كما هو الحال في الوظيفة السابقة ، يتم حساب مستوى الإيقاف المرتبط إما بسعر العرض أو وسيطة argOpenPrice. إذا كانت وسيطة argAdjustPrice داخل مستوى الإيقاف (أي غير صالحة) ، فسيتم ضبط السعر حتى يكون خارج مستوى الإيقاف بعدد النقاط المحدد بوسيطة argAddPips.

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

إضافة وقف الخسارة والربح المحتمل
Add Stop Loss and Take Profit

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

كود :

bool AddStopProfit(int argTicket, double argStopLoss, double argTakeProfit)
{
if(argStopLoss == 0 && argTakeProfit == 0) return(false);
OrderSelect(argTicket,SELECT_BY_TICKET);
double OpenPrice = OrderOpenPrice();
while(IsTradeCon****Busy()) Sleep(10);
// Modify Order
bool TicketMod = OrderModify(argTicket,OrderOpenPrice(),argStopLoss ,argTakeProfit,0);
// Error Handling
if(TicketMod == false)
{
int ErrorCode = GetLastError();
string ErrDesc = ErrorDescription(ErrorCode);
string ErrAlert = StringConcatenate("Add Stop/Profit - Error ",ErrorCode,": ",
ErrDesc);
Alert(ErrAlert);
string ErrLog = StringConcatenate("Bid: ",MarketInfo(OrderSymbol(),MODE_BID),
" Ask: ",MarketInfo(OrderSymbol(),MODE_ASK)," Ticket: ",argTicket,
" Stop: ",argStopLoss," Profit: ",argTakeProfit);
Print(ErrLog);
}
return(TicketMod);

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

دورة البرمجة في الفوركس للمبتدئين – الدرس السابع

دورة البرمجة في الفوركس للمبتدئين – الدرس التاسع

دورة