
مقدمه
این مقاله با پیگیری رویدادها[۱] در متاتریدر ۴، آنهم بهصورت برنامهریزیشده، سروکار دارد؛ رویدادهایی مانند، بازشدن، بستهشدن، یا اصلاح معاملات؛ و هدف مقاله، کاربرانی هستند که حداقل مهارتها در کار با نرمافزار و برنامهنویسی MQL4 را دارند.
رویداد به چه چیزی گفته میشود و اصلاً چرا باید پیگیر آن باشیم؟
برای اجرای برخی از استراتژیها، اینکه تنها بدانیم آیا اکسپرت پوزیشنی را باز کرده یا نه، کافی نیست. بعضیوقتها لازم است لحظهی بازشدن، بستهشدن، یا اصلاح یک پوزیشن، و یا شروع شدن یک معاملهی انتظاری را، سریع “دریابیم”. هیچ تابعی در MQL4 از قبل آماده نشده تا مشکل ما را حل کند. اما همهچیز برای ساختن چنین چیزی مهیاست و قصد داریم همین کار را بکنیم.
اصول تعریف رویداد
فرد چطور میتواند بفهمد که رویدادی اتفاق اُفتاده است؟ رویداد، بطور کلی، چیست؟ در تلاش برای پاسخگویی به این سوالات، به چنین نتیجهای خواهیم رسید: یک رویداد، تغییر وضعیت یک معامله/پوزیشن باز است. در اینجا، تغییر حجم پوزیشنهای باز، یا [تغییر] حد ضررِ یک پوزیشن را، میتوانیم مثال بزنیم.
فرد چطور میتواند رخ دادن یک رویداد را در لحظه شناسایی کند؟ جواب ساده است. برای انجام این کار، باید مقدار (value) را به حافظه بسپاریم، تا پیگیری شود (در اینجا، حجم پوزیشنها مدنظر است)، و سپس، برای مثال، در تیک بعدی، آن را با مقدارِ جدیدِ بدستآمده، مقایسه کنیم. بیایید یک اکسپرت ساده بنویسیم، تا ما را از تغییرات [بوجودآمده] در حجم پوزیشنها مطلع کند.
int start() { static bool first = true; static int pre_OrdersTotal = 0; int _OrdersTotal = OrdersTotal(); // If this is the first launch of the Expert, we don't know the amount of orders on the previous tick. // So just remeber it, mark that the first launch has been happened, and exit. if ( first ) { pre_OrdersTotal = _OrdersTotal; first = false; return(0); } // Compare the amount of positions on the previous tick with the current amount // If it has been changed, display message if ( _OrdersTotal > pre_OrdersTotal ) Alert( "The amount of positions increased! There were - ", pre_OrdersTotal, ", there are now - ", _OrdersTotal ); if ( _OrdersTotal < pre_OrdersTotal ) Alert( "The amount of positions decreased! There were - ", pre_OrdersTotal, ", there are now - ", _OrdersTotal ); // Memorize the amount of positions pre_OrdersTotal = _OrdersTotal; return(0); }
ضروری است که بعضی از ویژگیهای خاص این اکسپرت را خاطرنشان کنیم:
- متغیرهای first و pre_OrdersTotal، اِستاتیک اعلام شدهاند. بنابراین، مقادیر آنها هنگام تَرکِ تابع start() صفر نمیشود. جایگزین متغیرهای اِستاتیک، متغیرهای جهانی هستند (که فراتر از توابع اعلام شدهاند)، اما بسیاری از آنها باعث بههمریختگی و ادغام اسامی میشوند (فرد ممکن است اشتباهاً متغیری با نام مشابه، درون تابع، اعلام کند، که این باعث تداخل خواهد شد). بنابراین، تمام متغیرها را در بدنهی تابع اعلام میکنیم.
- اکسپرت، ما را دربارهی تغییرات در حجم پوزیشنهای باز، و همینطور، معاملات انتظاری، آگاه میسازد (تابع OrdersTotal()، حجم کلی آنها را برمیگرداند).
- در اینجا، اکسپرت، به ما چیزی دربارهی شروع شدنِ یک معاملهی انتظاری، نمیگوید زیرا مقدارِ OrdersTotal()، تغییر نخواهد کرد.
- وقتی اولین بار نرمافزار و اکسپرت را اجرا میکنیم، اکسپرت قادر به شناسایی تغییرات در حجم معاملات نیست زیرا “نمیداند” چه تعداد معامله روی تیک قبلی بوده است.
- پیام، فقط وقتی ظاهر میشود که برای جفتاَرزی که اکسپرت روی نمودار آن مشغول کار است، تیک جدیدی برسد. اکسپرت هیچ رویداد راهاندازی[۲] دیگری ندارد.
آخرین مشکل نیز با قرار دادن بدنهی تابع start در یک حلقه[۳]، برطرف میشود. بنابراین، روی هر تیک، بررسی صورت نمیگیرد، بلکه در بازههای زمانی خاصی شاهد آن خواهیم بود:
int start() { static bool first = true; static int pre_OrdersTotal = 0; int _OrdersTotal = OrdersTotal(); // If it is the first launch of the Expert Advisor, we do not know the amount of orders on the previous tick. // So we will just memorize it, check that the first launch has already happened, and exit. if ( first ) { pre_OrdersTotal = _OrdersTotal; first = false; return(0); } while ( !IsStopped() ) { _OrdersTotal = OrdersTotal(); // Compare the amount of positions on the previous tick to the current amount. // If it has been changed, display the message if ( _OrdersTotal > pre_OrdersTotal ) Alert( "The amount of positions increased! There were - ", pre_OrdersTotal, ", there are now - ", _OrdersTotal ); if ( _OrdersTotal < pre_OrdersTotal ) Alert( "The amount of positions decreased! There were - ", pre_OrdersTotal, ", there are now - ", _OrdersTotal ); // Memorize the amount of positions pre_OrdersTotal = _OrdersTotal; Sleep(100); } return(0); }
در نسخهی بالا، پیامِ مرتبط با تغییرات در حجم پوزیشنها، بلافاصله نمایش داده میشود: باور نداری برو چک کن!
فیلتر کردن رویدادها: معیارها
اکسپرت ما، ما را دربارهی پوزیشنهای جدیدی که روی تمام جفتاَرزها ظاهر میشوند، آگاه میسازد، همانطور که در حال حاضر هم اینگونه است. اما، رایجتر این است که فقط به اطلاعاتی در مورد تغییرات حجم در معاملاتِ جفتاَرز کنونی، نیاز داریم. علاوه بر این، معاملاتی که اکسپرت آنها را مدیریت میکند، اغلب رایج است که با یک MagicNumber علامتگذاری میشوند. پس بیایید رویدادها را با این دو معیار، فیلتر کنیم. بهعبارت دیگر، ما از تغییرات در حجم معاملات، فقط برای جفتاَرز کنونی، و فقط برای MagicNumber دادهشده، آگاه خواهیم شد.
extern int MagicNumber = 0; int start() { static bool first = true; static int pre_OrdersTotal = 0; int _OrdersTotal = 0, now_OrdersTotal = 0, _GetLastError = 0; while ( !IsStopped() ) { _OrdersTotal = OrdersTotal(); now_OrdersTotal = 0; for ( int z = _OrdersTotal - 1; z >= 0; z -- ) { if ( !OrderSelect( z, SELECT_BY_POS ) ) { _GetLastError = GetLastError(); Print( "OrderSelect( ", z, ", SELECT_BY_POS ) - Error #", _GetLastError ); continue; } // Count the amount of orders by the current symbol and with the specified MagicNumber if ( OrderMagicNumber() == MagicNumber && OrderSymbol() == Symbol() ) now_OrdersTotal ++; } // Display data only if this is not the first launch of the Expert if ( !first ) { // Compare the amount of positions on the previous tick with the current amount // If it has changed, display the message if ( now_OrdersTotal > pre_OrdersTotal ) Alert( Symbol(), ": amount of positions having MagicNumber ", MagicNumber, " increased! there were - ", pre_OrdersTotal, ", there are now - ", now_OrdersTotal ); if ( now_OrdersTotal < pre_OrdersTotal ) Alert( Symbol(), ": amount of positions having MagicNumber ", MagicNumber, " decreased! There were - ", pre_OrdersTotal, ", there are now - ", now_OrdersTotal ); } else { first = false; } //---- Remember the amount of positions pre_OrdersTotal = now_OrdersTotal; Sleep(100); } return(0); }
پالایش
البته، پیدا کردن حجم کلی معاملات خوب است، اما بعضیوقتها اطلاعات بیشتر و دقیقتری نیاز است، برای مثال، “پوزیشن Buy باز شد یاSell ؟”، “آیا معاملهی انتظاری شروع شد؟”، “پوزیشن با حد ضرر بسته شد، یا با حد سود، یا دستی؟”. بیایید لیست رویدادهایی که قرار است پیگیری شوند را تا جای ممکن کامل، و موارد را گروهبندی کنیم.
- باز کردن یک پوزیشن
- “پوزیشن بازار”
- Buy
- Sell
- معاملهی انتظاری
- Buy Limit
- Sell Limit
- Buy Stop
- Sell Stop
- “پوزیشن بازار”
- شروع معامله
- Buy Limit
- Sell Limit
- Buy Stop
- Sell Stop
- بستن یک پوزیشن
- “پوزیشن بازار”
- Buy
- حد ضرر
- حد سود
- دستی (نه حد ضرر، نه حد سود)
- Sell
- حد ضرر
- حد سود
- دستی
- معاملهی انتظاری (حذف)
- Buy Limit
- منقضی شدن
- دستی
- Sell Limit
- زمان انقضاء
- دستی
- Buy Stop
- منقضی شدن
- دستی
- Sell Stop
- منقضی شدن
- دستی
- Buy Limit
- Buy
- “پوزیشن بازار”
- اصلاح یک پوزیشن
- “پوزیشن بازار”
- Buy
- حد سود
- حد ضرر
- Sell
- حد سود
- حد ضرر
- معاملهی انتظاری
- Buy Limit
- قیمت بازشدن
- حد ضرر
- حد سود
- تاریخ انقضاء
- Sell Limit
- قیمت بازشدن
- حد ضرر
- حد سود
- تاریخ انقضاء
- Buy Stop
- قیمت بازشدن
- حد ضرر
- حد سود
- تاریخ انقضاء
- Sell Stop
- قیمت بازشدن
- حد ضرر
- حد سود
- تاریخ انقضاء
- Buy Limit
- Buy
- “پوزیشن بازار”
قبل از اینکه برویم سراغ الگوریتم، بیایید بررسی کنیم آیا تمام رویدادهای اشارهشده در بالا را واقعاً نیاز داریم یا خیر. اگر بخواهیم اکسپرتی بسازیم که ما را در مورد تمام تغییرات بوجودآمده در تمام پوزیشنها مطلع سازد، جواب بله است، و تمام این رویدادها را باید درنظر بگیریم. اما هدف ما متواضعانهتر است: میخواهیم کاری کنیم اکسپرت “بفهمد” برای پوزیشنهایی که دارد با آنها کار میکند، چه اتفاقاتی میاُفتند.
در اینجا، لیست را میشود بهطرز چشمگیری کوتاهتر کرد: بازشدن پوزیشن، درج معاملات انتظاری، تمام آیتمهای اصلاح و بستن دستی پوزیشنها را، میتوان از لیست حذف نمود – این رویدادها را خود اکسپرت تولید میکند (و نمیتوانند بدون اکسپرت اتفاق بیفتند). بنابراین، اکنون چنین لیستی خواهیم داشت:
- شروع شدن معامله
- Buy Limit
- Sell Limit
- Buy Stop
- Sell Stop
- بستن پوزیشن
- “پوزیشن بازار”
- Buy
- حد ضرر
- حد سود
- Sell
- حد ضرر
- حد سود
- معاملهی انتظاری (منقضی شدن)
- Buy Limit
- Sell Limit
- Buy Stop
- Sell Stop
- Buy
- “پوزیشن بازار”
اکنون دیگر در لیست ما جای شک و شبهه نیست و میتوانیم کدنویسی را شروع کنیم. فقط باید کمی احتیاط بهخرج دهیم زیرا راههای زیادی برای تعریف کردنِ نحوهی بستهشدن پوزیشن (حد سود، حد ضرر) وجود دارد:
- اگر حجم کلی پوزیشنها کاهش یافت، در بخش هیستوری، پوزیشنی که اخیراً بسته شدهاست را جستجو کرده و با کمک پارامترهای آن بفهمید چگونه بسته شدهاست، یا
- تیکتهای تمام پوزیشنهای باز را بهخاطر سپرده و سپس در بخش هیستوری، با تیکت آن، دنبال پوزیشن “ناپدیدشده” بگردید.
راه اول در اجرا سادهتر است اما ممکن است دادهی اشتباه تولید شود، اگر دو پوزیشن درون یک تیک مشابه بسته شوند—یکی دستی باشد و دیگری توسط StopLoss بسته شود—اکسپرت، با پیدا کردن آن پوزیشنی که زمان بستهشدنش نزدیکتر است، دو رویداد مشابه تولید خواهد کرد (اگر پوزیشن آخر دستی بسته شده باشد، هر دو رویداد، بستهشدن دستی تلقی خواهند شد). آنگاه اکسپرت “نخواهد دانست” که یکی از پوزیشنها توسط استاپ لاس بسته شدهاست.
بنابراین، برای جلوگیری از چنین مشکلاتی، بیایید تا حد امکان کد باسوادی بنویسیم!
extern int MagicNumber = 0; // open positions array as on the previous tick int pre_OrdersArray[][2]; // [amount of positions][ticket #, position type] int start() { // first launch flag static bool first = true; // last error code int _GetLastError = 0; // total amount of positions int _OrdersTotal = 0; // the amount of positions that meet the criteria (the current symbol and the MagicNumber), // as on the current tick int now_OrdersTotal = 0; // the amount of positions that meet the criteria (the current symbol and the specified MagicNumber), // as on the previous tick static int pre_OrdersTotal = 0; // open positions array as on the current tick int now_OrdersArray[][2]; // [# in the list][ticket #, position type] // the current number of the position in the array now_OrdersArray (for search) int now_CurOrder = 0; // the current number of the position in the array pre_OrdersArray (for search) int pre_CurOrder = 0; // array for storing the amount of closed positions of each type int now_ClosedOrdersArray[6][3]; // [order type][closing type] // array for storing the amount of triggered pending orders int now_OpenedPendingOrders[4]; // [order type] (there are only 4 types of pending orders totally) // temporary flags bool OrderClosed = true, PendingOrderOpened = false; // temporary variables int ticket = 0, type = -1, close_type = -1; //+------------------------------------------------------------------+ //| Infinite loop //+------------------------------------------------------------------+ while ( !IsStopped() ) { // memorize the total amount of positions _OrdersTotal = OrdersTotal(); // change the open positions array size for the current amount ArrayResize( now_OrdersArray, _OrdersTotal ); // zeroize the array ArrayInitialize( now_OrdersArray, 0.0 ); // zeroize the amount of positions met the criteria now_OrdersTotal = 0; // zeroize the arrays of closed positions and triggered orders ArrayInitialize( now_ClosedOrdersArray, 0.0 ); ArrayInitialize( now_OpenedPendingOrders, 0.0 ); //+------------------------------------------------------------------+ //| Search in all positions and write only those in the array that //| meet the criteria //+------------------------------------------------------------------+ for ( int z = _OrdersTotal - 1; z >= 0; z -- ) { if ( !OrderSelect( z, SELECT_BY_POS ) ) { _GetLastError = GetLastError(); Print( "OrderSelect( ", z, ", SELECT_BY_POS ) - Error #", _GetLastError ); continue; } // Count orders for the current symbol and with the specified MagicNumber if ( OrderMagicNumber() == MagicNumber && OrderSymbol() == Symbol() ) { now_OrdersArray[now_OrdersTotal][0] = OrderTicket(); now_OrdersArray[now_OrdersTotal][1] = OrderType(); now_OrdersTotal ++; } } // change the open positions array size for the amount of positions met the criteria ArrayResize( now_OrdersArray, now_OrdersTotal ); //+------------------------------------------------------------------+ //| Search in the list of positions on the previous tick and count //| how many positions have been closed and pending orders triggered //+------------------------------------------------------------------+ for ( pre_CurOrder = 0; pre_CurOrder < pre_OrdersTotal; pre_CurOrder ++ ) { // memorize the ticket and the order type ticket = pre_OrdersArray[pre_CurOrder][0]; type = pre_OrdersArray[pre_CurOrder][1]; // suppose that, if it is a position, it has been closed OrderClosed = true; // suppose that, if it is a pending order, it has not triggered PendingOrderOpened = false; // search in all positions from the current list of open positions for ( now_CurOrder = 0; now_CurOrder < now_OrdersTotal; now_CurOrder ++ ) { // if a position with this ticket is in the list, if ( ticket == now_OrdersArray[now_CurOrder][0] ) { // the position has not been closed (the order has not been cancelled) OrderClosed = false; // if its type has changed, if ( type != now_OrdersArray[now_CurOrder][1] ) { // it is a pending order that has triggered PendingOrderOpened = true; } break; } } // if a position has not been closed (the order has not been cancelled), if ( OrderClosed ) { // select it if ( !OrderSelect( ticket, SELECT_BY_TICKET ) ) { _GetLastError = GetLastError(); Print( "OrderSelect( ", ticket, ", SELECT_BY_TICKET ) - Error #", _GetLastError ); continue; } // and check HOW the position has been closed (the order has been cancelled): if ( type < 2 ) { // Buy and Sell: 0 - manually, 1 - by SL, 2 - by TP close_type = 0; if ( StringFind( OrderComment(), "[sl]" ) >= 0 ) close_type = 1; if ( StringFind( OrderComment(), "[tp]" ) >= 0 ) close_type = 2; } else { // Pending orders: 0 - manually, 1 - expiration close_type = 0; if ( StringFind( OrderComment(), "expiration" ) >= 0 ) close_type = 1; } // and write in the closed orders array that the order of type 'type' // was cancelled as close_type now_ClosedOrdersArray[type][close_type] ++; continue; } // if a pending order has triggered, if ( PendingOrderOpened ) { // write in the triggered orders array that the order of type 'type' has triggered now_OpenedPendingOrders[type-2] ++; continue; } } //+------------------------------------------------------------------+ //| Collected all necessary information - display it //+------------------------------------------------------------------+ // if it is not the first launch of the Expert Advisor if ( !first ) { // search in all elements of the triggered pending orders array for ( type = 2; type < 6; type ++ ) { // if the element is not empty (an order of the type has triggered), display information if ( now_OpenedPendingOrders[type-2] > 0 ) Alert( Symbol(), ": triggered ", _OrderType_str( type ), " order!" ); } // search in all elements of the closed positions array for ( type = 0; type < 6; type ++ ) { for ( close_type = 0; close_type < 3; close_type ++ ) { // if the element is not empty (the position has been closed), display information if ( now_ClosedOrdersArray[type][close_type] > 0 ) CloseAlert( type, close_type ); } } } else { first = false; } //---- save the current positions array in the previous positions array ArrayResize( pre_OrdersArray, now_OrdersTotal ); for ( now_CurOrder = 0; now_CurOrder < now_OrdersTotal; now_CurOrder ++ ) { pre_OrdersArray[now_CurOrder][0] = now_OrdersArray[now_CurOrder][0]; pre_OrdersArray[now_CurOrder][1] = now_OrdersArray[now_CurOrder][1]; } pre_OrdersTotal = now_OrdersTotal; Sleep(100); } return(0); } void CloseAlert( int alert_type, int alert_close_type ) { string action = ""; if ( alert_type < 2 ) { switch ( alert_close_type ) { case 1: action = " by StopLoss!"; break; case 2: action = " by TakeProfit!"; break; default: action = " manually!"; break; } Alert( Symbol(), ": ", _OrderType_str( alert_type ), "-position closed", action ); } else { switch ( alert_close_type ) { case 1: action = " by expiration!"; break; default: action = " manually!"; break; } Alert( Symbol(), ": ", _OrderType_str( alert_type ), "-order cancelled", action ); } } // returns OrderType as a text string _OrderType_str( int _OrderType ) { switch ( _OrderType ) { case OP_BUY: return("Buy"); case OP_SELL: return("Sell"); case OP_BUYLIMIT: return("BuyLimit"); case OP_BUYSTOP: return("BuyStop"); case OP_SELLLIMIT: return("SellLimit"); case OP_SELLSTOP: return("SellStop"); default: return("UnknownOrderType"); } }
ادغام در اکسپرتها و کاربرد آن
برای اینکه در هر اکسپرتی از “تلهی رویداد” راحت استفاده کنیم، بیایید کد را در فایل Events.mq4 قرار داده تا با دستور #include آن را درون اکسپرتها اضافه کنیم. برای انجام این کار:
- کد را بهشکل یک تابع درآورید، تا پس از این، از اکسپرتها فراخوانده شود؛
- متغیر جهانی MagicNumber را حذف کرده و پارامتر تابع magic را اضافه کنید (همان نقش را ایفا خواهند کرد، و این کار را جهت مسدود نشدن لیست متغیرهای اکسترنال اکسپرت، انجام میدهیم)؛
- برای هر رویداد، یک متغیر جهانی اضافه کنید – این کار کاربرد آنها را راحتتر میکند (همچنین باید صفر کردن این متغیرها را در تابع start وارد کنیم)؛
- حلقهی بینهایت را حذف کنید – اکنون “نمونهگیری” بین فراخوانهای تابع انجام خواهد شد (بهعبارت دیگر، با فراخوانی یک تابع، لیستی از تغییرات را، در مقایسه با فراخوان قبلی تابع، دریافت میکنیم)؛
- هشدارها را حذف کنید، در صورت نیاز میشود آنها را به اکسپرت اضافه کرد؛
- تمام موارد بالا را درنظر گرفته و کد را صیقل دهید.
و این چیزی است که باید به آن دست یابیم:
// array of open positions as it was on the previous tick int pre_OrdersArray[][2]; // [amount of positions][ticket #, positions type] // variables of events int eventBuyClosed_SL = 0, eventBuyClosed_TP = 0; int eventSellClosed_SL = 0, eventSellClosed_TP = 0; int eventBuyLimitDeleted_Exp = 0, eventBuyStopDeleted_Exp = 0; int eventSellLimitDeleted_Exp = 0, eventSellStopDeleted_Exp = 0; int eventBuyLimitOpened = 0, eventBuyStopOpened = 0; int eventSellLimitOpened = 0, eventSellStopOpened = 0; void CheckEvents( int magic = 0 ) { // flag of the first launch static bool first = true; // the last error code int _GetLastError = 0; // total amount of positions int _OrdersTotal = OrdersTotal(); // the amount of positions met the criteria (the current symbol and the specified MagicNumber), // as it is on the current tick int now_OrdersTotal = 0; // the amount of positions met the criteria as on the previous tick static int pre_OrdersTotal = 0; // array of open positions as of the current tick int now_OrdersArray[][2]; // [# in the list][ticket #, position type] // the current number of the position in array now_OrdersArray (for searching) int now_CurOrder = 0; // the current number of the position in array pre_OrdersArray (for searching) int pre_CurOrder = 0; // array for storing the amount of closed positions of each type int now_ClosedOrdersArray[6][3]; // [order type][closing type] // array for storing the amount of triggered pending orders int now_OpenedPendingOrders[4]; // [order type] // temporary flags bool OrderClosed = true, PendingOrderOpened = false; // temporary variables int ticket = 0, type = -1, close_type = -1; //zeroize the variables of events eventBuyClosed_SL = 0; eventBuyClosed_TP = 0; eventSellClosed_SL = 0; eventSellClosed_TP = 0; eventBuyLimitDeleted_Exp = 0; eventBuyStopDeleted_Exp = 0; eventSellLimitDeleted_Exp = 0; eventSellStopDeleted_Exp = 0; eventBuyLimitOpened = 0; eventBuyStopOpened = 0; eventSellLimitOpened = 0; eventSellStopOpened = 0; // change the open positions array size for the current amount ArrayResize( now_OrdersArray, MathMax( _OrdersTotal, 1 ) ); // zeroize the array ArrayInitialize( now_OrdersArray, 0.0 ); // zeroize arrays of closed positions and triggered orders ArrayInitialize( now_ClosedOrdersArray, 0.0 ); ArrayInitialize( now_OpenedPendingOrders, 0.0 ); //+------------------------------------------------------------------+ //| Search in all positions and write in the array only those //| meeting the criteria //+------------------------------------------------------------------+ for ( int z = _OrdersTotal - 1; z >= 0; z -- ) { if ( !OrderSelect( z, SELECT_BY_POS ) ) { _GetLastError = GetLastError(); Print( "OrderSelect( ", z, ", SELECT_BY_POS ) - Error #", _GetLastError ); continue; } // Count the amount of orders on the current symbol with the specified MagicNumber if ( OrderMagicNumber() == magic && OrderSymbol() == Symbol() ) { now_OrdersArray[now_OrdersTotal][0] = OrderTicket(); now_OrdersArray[now_OrdersTotal][1] = OrderType(); now_OrdersTotal ++; } } // change the open positions array size for the amount of positions meeting the criteria ArrayResize( now_OrdersArray, MathMax( now_OrdersTotal, 1 ) ); //+-------------------------------------------------------------------------------------------------+ //| Search in the list of the previous tick positions and count how many positions have been closed //| and pending orders triggered //+-------------------------------------------------------------------------------------------------+ for ( pre_CurOrder = 0; pre_CurOrder < pre_OrdersTotal; pre_CurOrder ++ ) { // memorize the ticket number and the order type ticket = pre_OrdersArray[pre_CurOrder][0]; type = pre_OrdersArray[pre_CurOrder][1]; // assume that, if it is a position, it has been closed OrderClosed = true; // assume that, if it is a pending order, it has not triggered PendingOrderOpened = false; // search in all positions from the current list of open positions for ( now_CurOrder = 0; now_CurOrder < now_OrdersTotal; now_CurOrder ++ ) { // if there is a position with such a ticket number in the list, if ( ticket == now_OrdersArray[now_CurOrder][0] ) { // it means that the position has not been closed (the order has not been cancelled) OrderClosed = false; // if its type has changed, if ( type != now_OrdersArray[now_CurOrder][1] ) { // it means that it was a pending order and it triggered PendingOrderOpened = true; } break; } } // if a position has been closed (an order has been cancelled), if ( OrderClosed ) { // select it if ( !OrderSelect( ticket, SELECT_BY_TICKET ) ) { _GetLastError = GetLastError(); Print( "OrderSelect( ", ticket, ", SELECT_BY_TICKET ) - Error #", _GetLastError ); continue; } // and check HOW the position has been closed (the order has been cancelled): if ( type < 2 ) { // Buy and Sell: 0 - manually, 1 - by SL, 2 - by TP close_type = 0; if ( StringFind( OrderComment(), "[sl]" ) >= 0 ) close_type = 1; if ( StringFind( OrderComment(), "[tp]" ) >= 0 ) close_type = 2; } else { // Pending orders: 0 - manually, 1 - expiration close_type = 0; if ( StringFind( OrderComment(), "expiration" ) >= 0 ) close_type = 1; } // and write in the closed orders array that the order of the type 'type' // was closed by close_type now_ClosedOrdersArray[type][close_type] ++; continue; } // if a pending order has triggered, if ( PendingOrderOpened ) { // write in the triggered orders array that the order of type 'type' triggered now_OpenedPendingOrders[type-2] ++; continue; } } //+--------------------------------------------------------------------------------------------------+ //| All necessary information has been collected - assign necessary values to the variables of events //+--------------------------------------------------------------------------------------------------+ // if it is not the first launch of the Expert Advisor if ( !first ) { // search in all elements of the triggered pending orders array for ( type = 2; type < 6; type ++ ) { // if the element is not empty (an order of the type has not triggered), change the variable value if ( now_OpenedPendingOrders[type-2] > 0 ) SetOpenEvent( type ); } // search in all elements of the closed positions array for ( type = 0; type < 6; type ++ ) { for ( close_type = 0; close_type < 3; close_type ++ ) { // if the element is not empty (a position has been closed), change the variable value if ( now_ClosedOrdersArray[type][close_type] > 0 ) SetCloseEvent( type, close_type ); } } } else { first = false; } //---- save the current positions array in the previous positions array ArrayResize( pre_OrdersArray, MathMax( now_OrdersTotal, 1 ) ); for ( now_CurOrder = 0; now_CurOrder < now_OrdersTotal; now_CurOrder ++ ) { pre_OrdersArray[now_CurOrder][0] = now_OrdersArray[now_CurOrder][0]; pre_OrdersArray[now_CurOrder][1] = now_OrdersArray[now_CurOrder][1]; } pre_OrdersTotal = now_OrdersTotal; } void SetOpenEvent( int SetOpenEvent_type ) { switch ( SetOpenEvent_type ) { case OP_BUYLIMIT: eventBuyLimitOpened ++; return(0); case OP_BUYSTOP: eventBuyStopOpened ++; return(0); case OP_SELLLIMIT: eventSellLimitOpened ++; return(0); case OP_SELLSTOP: eventSellStopOpened ++; return(0); } } void SetCloseEvent( int SetCloseEvent_type, int SetCloseEvent_close_type ) { switch ( SetCloseEvent_type ) { case OP_BUY: { if ( SetCloseEvent_close_type == 1 ) eventBuyClosed_SL ++; if ( SetCloseEvent_close_type == 2 ) eventBuyClosed_TP ++; return(0); } case OP_SELL: { if ( SetCloseEvent_close_type == 1 ) eventSellClosed_SL ++; if ( SetCloseEvent_close_type == 2 ) eventSellClosed_TP ++; return(0); } case OP_BUYLIMIT: { if ( SetCloseEvent_close_type == 1 ) eventBuyLimitDeleted_Exp ++; return(0); } case OP_BUYSTOP: { if ( SetCloseEvent_close_type == 1 ) eventBuyStopDeleted_Exp ++; return(0); } case OP_SELLLIMIT: { if ( SetCloseEvent_close_type == 1 ) eventSellLimitDeleted_Exp ++; return(0); } case OP_SELLSTOP: { if ( SetCloseEvent_close_type == 1 ) eventSellStopDeleted_Exp ++; return(0); } } }
اکنون رویدادها را میتوان از هر اکسپرتی، تنها با فعال کردن یک کتابخانه (library)، پیگیری کرد. مثالی از چنین اکسپرتی (EventsExpert.mq4) اینجا آورده شدهاست:
extern int MagicNumber = 0; #include <Events.mq4> int start() { CheckEvents( MagicNumber ); if ( eventBuyClosed_SL > 0 ) Alert( Symbol(), ": Buy position was closed by StopLoss!" ); if ( eventBuyClosed_TP > 0 ) Alert( Symbol(), ": Buy position was closed by TakeProfit!" ); if ( eventBuyLimitOpened > 0 || eventBuyStopOpened > 0 || eventSellLimitOpened > 0 || eventSellStopOpened > 0 ) Alert( Symbol(), ": pending order triggered!" ); return(0); }
نتیجهگیری
در این مقاله، راههای پیگیریِ برنامهریزیشدهی رویدادها را در متاتریدر ۴ با استفاده از ابزارهای MQL4 بررسی کردیم. تمام رویدادها را در سه گروه تقسیمبندی کردیم و آنها را با توجه به معیارهای از پیش تعیین شده، فیلتر کردیم. همچنین کتابخانهای ساختیم که به ما اجازه میدهد برخی از رویدادها را از هر اکسپرتی، بهسادگی پیگیری کنیم.
تابع CheckEvents() را میتوان کامل کرد (یا بهعنوان تمپلیت از آن استفاده کرد) تا رویدادهای دیگری که در این مقاله به آنها پرداخته نشد را پیگیری نمود.
این مقاله دارای فایل پیوست است.
[۱]) Events
[۲]) Launching events
[۳]) Loop
۳۸ مورد نظر
best ed pills online
herbal remedies for ed cialis without a doctor’s prescription ed treatments that really work
iv prednisone generic prednisone prednisone 5 mg tablet cost
can i buy cialis over the counter at walgreens cheapest cialis usa buy cialis toronto
how can i buy cialis online cialis cialis sale
how to get prednisone without a prescription buy prednisone online ۱۰ mg prednisone
buy prednisone tablets online generic prednisone how to buy prednisone online
pills erectile dysfunction drugs for ed best male enhancement pills
stromectol drops ivermectin for bed bugs ivermectin 12 mg tablet buy
best non prescription ed pills canadian pharmacy online pet antibiotics without vet prescription
pour-on ivermectin for goats ivermectin candida ivermectin pinworms
propeciaforlesscom cheapest propecia online generic propecia prescription
ivermectin dosing ivermectin solubility ivermectin side effects scabies
how to get prescription drugs without doctor prescription drugs online without legal to buy prescription drugs without prescription
canadian online drugstore prescription drugs canada buy online how can i order prescription drugs without a doctor
pharmacy without dr prescriptions ordering prescription drugs from canada reputable canadian mail order pharmacies
buy stromectol online stromectol for sale stromectol pills for humans
top rated ed pills ed medications list best ed pills non prescription
order provigil 100mg pill order generic modafinil
erythromycin ophth ointment 3.5 gm
trusted india online pharmacies reputable online pharmacies in india india pharmacies online
order modafinil 100mg for sale
stromectol for sale stromectol 12 mg tablets stromectol pills for humans
provigil 100mg pills oral modafinil 200mg
buy clomid clomid for sale canada clomid
buy modafinil 200mg for sale provigil us
lowest price tadalafil cialis 20mg cialis coupon
modafinil tablet buy provigil 100mg pill
stromectol stromectol 3 mg tablets price stromectol for humans for sale
purchase modafinil online cheap buy modafinil online
price for viagra sildenafil citrate tablets 100 mg sildenafil citrate tablets 100 mg
order modafinil online cheap buy modafinil 100mg generic
viagra pills sildenafil 100 mg sildenafil citrate 100mg for sale
order provigil order provigil 200mg pill provigil 100mg tablet
order modafinil 200mg generic
prescribing stromectol stromectol price usa stromectol for sale
buy erythromycin online http://erythromycin1m.com/#
generic provigil 200mg provigil online buy provigil 100mg for sale