رویـداد‌هـا در مـتـاتـریـدر ۴

رویـداد‌هـا در مـتـاتـریـدر 4

مقدمه

این مقاله با پیگیری رویدادها[۱] در متاتریدر ۴، آن‌هم به‌صورت برنامه‌ریزی‌شده، سروکار دارد؛ رویدادهایی مانند، بازشدن، بسته‌شدن، یا اصلاح معاملات؛ و هدف مقاله، کاربرانی هستند که حداقل مهارت‌ها در کار با نرم‌افزار و برنامه‌نویسی 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 ؟”، “آیا معامله‌ی انتظاری شروع شد؟”، “پوزیشن با حد ضرر بسته شد، یا با حد سود، یا دستی؟”. بیایید لیست رویدادهایی که قرار است پیگیری شوند را تا جای ممکن کامل، و موارد را گروه‌بندی کنیم.

  1. باز کردن یک پوزیشن
    • “پوزیشن بازار”
      • Buy
      • Sell
    • معامله‌ی انتظاری
      • Buy Limit
      • Sell Limit
      • Buy Stop
      • Sell Stop
  1. شروع معامله
    • Buy Limit
    • Sell Limit
    • Buy Stop
    • Sell Stop
  2. بستن یک پوزیشن
    • “پوزیشن بازار”
      • Buy
        • حد ضرر
        • حد سود
        • دستی (نه حد ضرر، نه حد سود)
      • Sell
        • حد ضرر
        • حد سود
        • دستی
      • معامله‌ی انتظاری (حذف)
        • Buy Limit
          • منقضی شدن
          • دستی
        • Sell Limit
          • زمان انقضاء
          • دستی
        • Buy Stop
          • منقضی شدن
          • دستی
        • Sell Stop
          • منقضی شدن
          • دستی
  1. اصلاح یک پوزیشن
    • “پوزیشن بازار”
      • Buy
        • حد سود
        • حد ضرر
      • Sell
        • حد سود
        • حد ضرر
      • معامله‌ی انتظاری
        • Buy Limit
          • قیمت بازشدن
          • حد ضرر
          • حد سود
          • تاریخ انقضاء
        • Sell Limit
          • قیمت بازشدن
          • حد ضرر
          • حد سود
          • تاریخ انقضاء
        • Buy Stop
          • قیمت بازشدن
          • حد ضرر
          • حد سود
          • تاریخ انقضاء
        • Sell Stop
          • قیمت بازشدن
          • حد ضرر
          • حد سود
          • تاریخ انقضاء

قبل از اینکه برویم سراغ الگوریتم، بیایید بررسی کنیم آیا تمام رویدادهای اشاره‌شده در بالا را واقعاً نیاز داریم یا خیر. اگر بخواهیم اکسپرتی بسازیم که ما را در مورد تمام تغییرات بوجود‌آمده در تمام پوزیشن‌ها مطلع سازد، جواب بله است، و تمام این رویدادها را باید درنظر بگیریم. اما هدف ما متواضعانه‌تر است: می‌خواهیم کاری کنیم اکسپرت “بفهمد” برای پوزیشن‌هایی که دارد با آن‌ها کار می‌کند، چه اتفاقاتی می‌اُفتند.

در اینجا، لیست را می‌شود به‌طرز چشمگیری کوتاه‌تر کرد: بازشدن پوزیشن، درج معاملات انتظاری، تمام آیتم‌های اصلاح و بستن دستی پوزیشن‌ها را، می‌توان از لیست حذف نمود – این رویدادها را خود اکسپرت تولید می‌کند (و نمی‌توانند بدون اکسپرت اتفاق بیفتند). بنابراین، اکنون چنین لیستی خواهیم داشت:

  1. شروع شدن معامله
  • Buy Limit
  • Sell Limit
  • Buy Stop
  • Sell Stop
  1. بستن پوزیشن
    • “پوزیشن بازار”
      • Buy
        • حد ضرر
        • حد سود
      • Sell
        • حد ضرر
        • حد سود
      • معامله‌ی انتظاری (منقضی شدن)
        • Buy Limit
        • Sell Limit
        • Buy Stop
        • Sell Stop

اکنون دیگر در لیست ما جای شک و شبهه نیست و می‌توانیم کدنویسی را شروع کنیم. فقط باید کمی احتیاط به‌خرج دهیم زیرا راه‌های زیادی برای تعریف کردنِ نحوه‌ی بسته‌شدن پوزیشن (حد سود، حد ضرر) وجود دارد:

  • اگر حجم کلی پوزیشن‌ها کاهش یافت، در بخش هیستوری، پوزیشنی که اخیراً بسته شده‌است را جستجو کرده و با کمک پارامترهای آن بفهمید چگونه بسته شده‌است، یا
  • تیکت‌های تمام پوزیشن‌های باز را به‌خاطر سپرده و سپس در بخش هیستوری، با تیکت آن، دنبال پوزیشن “ناپدیدشده” بگردید.

راه اول در اجرا ساده‌تر است اما ممکن است داده‌‌ی اشتباه تولید شود، اگر دو پوزیشن درون یک تیک مشابه بسته شوند—یکی دستی باشد و دیگری توسط 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

M23admin

→ خواندن مطلب قبلی

روشی برای ترسیم خطوط ساپورت و رزیستنس

خواندن مطلب بعدی ←

الگویی برای تریلینگ اِستاپ و خروج از بازار

۳۸ مورد نظر

نوشتن نظر شما

نشانی ایمیل شما منتشر نخواهد شد. بخش‌های موردنیاز علامت‌گذاری شده‌اند *