English Русский 中文 Español Deutsch 日本語 Português 한국어 Français Italiano
OnTrade() fonksiyonunu kullanarak Uzman Danışmanda (EA) alım satım etkinliklerinin işlenmesi

OnTrade() fonksiyonunu kullanarak Uzman Danışmanda (EA) alım satım etkinliklerinin işlenmesi

MetaTrader 5Örnekler | 9 Aralık 2021, 10:41
71 0
KlimMalgin
KlimMalgin

Giriş

MQL'de Uzmanları yazan herhangi bir yatırımcı, er ya da geç, Uzmanların nasıl çalıştığını bildirme zorunluluğuyla karşı karşıya kalacaktır. Ya da Uzmanın eylemleri hakkında SMS veya e-posta bildirimi uygulaması gerekebilir. Her durumda, piyasada meydana gelen belirli etkinlikleri veya bir uzman tarafından yapılan eylemleri "yakalamak" ve kullanıcıları bilgilendirmek zorundayız.

Bu yazıda size alım satım etkinliklerinin işlenmesini nasıl uygulayabileceğinizi anlatmak ve size uygulamamı sunmak istiyorum.

Bu makalede aşağıdaki etkinliklerin işlenmesini ele alacağız:

  • Pozisyonlar
    1. Ekle
    2. Değiştir (Zararı Durdur ve Kar Al’ı değiştir)
    3. Ters
    4. Tüm pozisyonu kapat
    5. Pozisyonun bir kısmını kapat
  • Bekleyen Emirler
    1. Yerleştir
    2. Değiştir

1. Bu nasıl çalışır? 

Başlamadan önce, alım satım etkinliklerinin nasıl işlediğini genel hatlarıyla anlatacağım ve gerekli tüm detayları anında açıklayacağım.

MQL5'te önceden tanımlanmış ve özel etkinlikler vardır. Özellikle Alım Satım etkinliğinde önceden tanımlanmış olanlarla ilgileniyoruz.

Alım Satım etkinliği, alım satım işlemi tamamlandığında her zaman oluşturulur. Alım Satım etkinliğinin oluşturulmasından sonra her defasında OnTrade() fonksiyonu çağrılır. Emirlerin ve pozisyonların işlenmesi tam olarak OnTrade() fonksiyonu içinde yapılacaktır.

2. Uzman Şablonu

Şimdi yeni bir Uzman Danışman (EA) oluşturalım. MetaEditor'da MQL5 Sihirbazını başlatmak için Dosya -> Yeni düğmesine tıklayın. Uzman Danışmanı (EA) seçin ve İleri düğmesine tıklayın. "Uzman Danışmanın (EA) genel özellikleri" iletişim kutusunda, Uzman Danışmanın (EA) Adını ve gerekirse kendi verilerinizi girin. Uzman Danışmanımı (EA) "TradeControl" olarak adlandırdım. Bu adı alabilir veya kendi adınızı seçebilirsiniz, önemli değil. Bir uzman yazarken anında oluşturulacağı için herhangi bir parametre belirtmeyeceğiz.

Tamamlandı! Uzman Danışman (EA) şablonu oluşturuldu, içine OnTrade() fonksiyonunu eklememiz gerekiyor.

Sonuç olarak, aşağıdaki kodu almalısınız:

//+------------------------------------------------------------------+
//|                                              TradeControl_en.mq5 |
//|                                             Copyright KlimMalgin |
//|                                                                  |
//+------------------------------------------------------------------+
#property copyright "KlimMalgin"
#property link      ""
#property version   "1.00"
//+------------------------------------------------------------------+
//| Expert initialization function                                   |
//+------------------------------------------------------------------+
int OnInit()
  {
//---
   
//---
   return(0);
  }
//+------------------------------------------------------------------+
//| Expert deinitialization function                                 |
//+------------------------------------------------------------------+
void OnDeinit(const int reason)
  {
//---
   
  }
//+------------------------------------------------------------------+
//| OnTrade function                                                 |
//+------------------------------------------------------------------+
void OnTrade()
  {
//---

//---
  }
//+------------------------------------------------------------------+
//| Expert tick function                                             |
//+------------------------------------------------------------------+
void OnTick()
  {
//---
   
  }
//+------------------------------------------------------------------+

3. Pozisyonlarla çalışma

En basit alım satım etkinliğiyle başlayalım - pozisyonları açma ve kapatma. Öncelikle "Satış" ve "Alış" düğmelerine bastıktan sonra hangi işlemlerin gerçekleştiğini anlamalısınız.

OnTrade() fonksiyonuna bir çağrı yaparsak:

Alert("The Trade event occurred");

Ardından, OnTrade() piyasa fonksiyonu tarafından açıldıktan sonra ve bununla birlikte Uyarımızın dört kez yürütüldüğünü göreceğiz:

Şekil 1. Uyarılar

Şekil 1. Uyarılar

OnTrade() neden dört kez çağrılır ve bu uyarılara nasıl yanıt verebiliriz? Bunu anlamak için belgelere bakalım:

 OnTrade

Fonksiyon, Alım Satım etkinliği meydana geldiğinde çağrılır. Bu, verilen emirlerin listesi, açılan pozisyonlar, emir geçmişi ve sözleşme geçmişi değiştirildiğinde gerçekleşir.

Burada bir şeyden bahsetmeliyim:

Bu makaleyi yazarken ve geliştiricilerle iletişim kurarken, geçmişteki değişikliklerin OnTrade() çağrısına yol açmadığını gördüm! Gerçek şu ki, OnTrade() fonksiyonu yalnızca verilen emirlerin ve açılan pozisyonların listesi değiştirildiğinde çağrılır! Alım satım etkinlikleri işleyicisini geliştirirken, yürütülen emirlerin ve sözleşmelerin geçmişte gecikmeli olarak görünebileceği gerçeğiyle karşılaşabilirsiniz ve OnTrade() fonksiyonları çalışırken bunları işleyemezsiniz.  

Şimdi etkinliklere dönelim. Gördüğümüz gibi, piyasaya göre açtığınızda, Alım Satım etkinliği 4 kez gerçekleşir:

  1. Piyasaya göre açılacak emir oluşturma.
  2. Sözleşme yürütme.
  3. Tamamlanan emri geçmişe iletme.
  4. Pozisyon Açılışı.

  Bu işlemi terminalde takip etmek için MetaTrader penceresinin "Alım Satım" sekmesindeki emir listesine dikkat edin: 

Şekil 2. "Alım Satım" sekmesindeki emirlerin listesi

Şekil 2. "Alım Satım" sekmesindeki emirlerin listesi

Bir pozisyon açtığınızda (ör. aşağı), emirler listesinde başlatıldı durumuna sahip bir emir görünür (Şekil 2). Bu, verilen emirlerin listesini değiştirir ve Alım Satım etkinliği çağrılır. OnTrade() fonksiyonu ilk kez etkinleştirildi. Daha sonra oluşturulan emre göre bir sözleşme yürütülür. Bu aşamada OnTrade() fonksiyonu ikinci kez yürütülür. Sözleşme gerçekleşir gerçekleşmez tamamlanan emir ve gerçekleştirilen sözleşme geçmişe gönderilir ve OnTrade() fonksiyonu üçüncü kez çağrılır. Son aşamada gerçekleştirilen sözleşme ile bir pozisyon açılır ve OnTrade() fonksiyonu dördüncü kez çağrılır.

Pozisyon açılış anını "yakalamak" için, OnTrade() fonksiyonunu her çağırdığınızda, emir listesini, emir geçmişini ve sözleşme geçmişini analiz etmeniz gerekir. Şimdi yapacağımız şey budur!

Tamam, OnTrade() fonksiyonu çağrılır ve "Alım Satım" sekmesinde emir sayısının değişip değişmediğini bilmemiz gerekir. Bunu yapmak için, önceki OnTrade() çağrısı sırasındaki ve şimdiki listedeki emir sayısını karşılaştırmamız gerekir. Şu anda listede kaç emir olduğunu bulmak için OrdersTotal() fonksiyonunu kullanacağız. Ve önceki çağrıda kaç emrin listelendiğini bilmek için, her OnTrade() çağrısında OrdersTotal() değerini tutmamız gerekecektir. Bunun için özel bir değişken oluşturacağız:

int OrdersPrev = 0;        // Number of orders at the time of previous OnTrade() call

OnTrade() fonksiyonunun sonunda OrdersPrev değişkenine OrdersTotal() değeri atanacaktır.

Uzman Danışmanı (EA) çalıştırdığınızda da durumu göz önünde bulundurmalısınız ve zaten listede bekleyen emirler vardır. Uzman bunları tespit edebilmelidir, bu nedenle OnInit() fonksiyonunda OrdersPrev değişkenine de OrdersTotal() değeri atanmalıdır. Uzmanda yaptığımız değişiklikler aşağıdaki gibi görünecektir: 

int OrdersPrev = 0;        // Number of orders at the time of previous OnTrade() call


//+------------------------------------------------------------------+
//| Expert initialization function                                   |
//+------------------------------------------------------------------+
int OnInit()
  {
//---
   OrdersPrev = OrdersTotal();
//---
   return(0);
  }
//+------------------------------------------------------------------+
//| OnTrade function                                                 |
//+------------------------------------------------------------------+
void OnTrade()
  {
//---

OrdersPrev = OrdersTotal();
//---
  }

Artık mevcut ve önceki çağrılar için emirlerin sayısını bildiğimize göre - emrin listede ne zaman göründüğünü ve herhangi bir nedenle ne zaman ortadan kaybolduğunu öğrenebiliriz. Bunu yapmak için aşağıdaki koşulu kullanacağız:

if (OrdersPrev < OrdersTotal())
{
  // Order appeared
}
else if(OrdersPrev > OrdersTotal())
{
  // Order disappeared
}

Bu nedenle, bir önceki çağrıda şimdikinden daha az emrimiz varsa, emir listede görünür (birden fazla emir aynı anda görünemez), ancak tersi olursa, yani şimdi önceki OnTrade() çağrısından daha az emrimiz varsa, emir bir nedenle yürütülür veya iptal edilir. Pozisyonlarla yapılan hemen hemen tüm çalışmalar bu iki koşulla başlar.

Sadece Zararı Durdur ve Kar Al ayrı bir çalışma gerektirir. OnTrade() fonksiyonuna pozisyonlarla çalışan kodu ekleyeceğim. Bunu bir düşünelim:

void OnTrade()
  {
//---
Alert("Trade event occurred");

HistorySelect(start_date,TimeCurrent());

if (OrdersPrev < OrdersTotal())
{
   OrderGetTicket(OrdersTotal()-1);// Select the last order to work with
   _GetLastError=GetLastError();
   Print("Error #",_GetLastError);ResetLastError();
   //--
   if (OrderGetInteger(ORDER_STATE) == ORDER_STATE_STARTED)
   {
      Alert(OrderGetTicket(OrdersTotal()-1),"Order has arrived for processing");
      LastOrderTicket = OrderGetTicket(OrdersTotal()-1);    // Saving the order ticket for further work
   }
   
}
else if(OrdersPrev > OrdersTotal())
{
   state = HistoryOrderGetInteger(LastOrderTicket,ORDER_STATE);

   // If order is not found, generate an error
   _GetLastError=GetLastError();
   if (_GetLastError != 0){Alert("Error #",_GetLastError," Order is not found!");LastOrderTicket = 0;}
   Print("Error #",_GetLastError," state: ",state);ResetLastError();


   // If order is fully executed
   if (state == ORDER_STATE_FILLED)
   {
      // Then analyze the last deal
      // --
      Alert(LastOrderTicket, "Order executed, going to deal");
      switch(HistoryDealGetInteger(HistoryDealGetTicket(HistoryDealsTotal()-1),DEAL_ENTRY))
      {
         
         // Entering the market
         case DEAL_ENTRY_IN:
         Alert(HistoryDealGetInteger(HistoryDealGetTicket(HistoryDealsTotal()-1),DEAL_ORDER), 
         " order invoked deal #",HistoryDealGetTicket(HistoryDealsTotal()-1));
         
            switch(HistoryDealGetInteger(HistoryDealGetTicket(HistoryDealsTotal()-1),DEAL_TYPE))
            {
               case 0:
               // If volumes of position and deal are equal, then position has just been opened
                  if (PositionSelect(HistoryDealGetString(HistoryDealGetTicket(HistoryDealsTotal()-1),DEAL_SYMBOL)) 
                  && (PositionGetDouble(POSITION_VOLUME) == HistoryDealGetDouble(HistoryDealGetTicket(HistoryDealsTotal()-1),DEAL_VOLUME)))
                  {
                     Alert("Buy position has been opened on pair ", 
                           HistoryDealGetString(HistoryDealGetTicket(HistoryDealsTotal()-1),DEAL_SYMBOL)); 
                  }
                  else
               // If volumes of position and deal are not equal, then position has been incremented
                  if (PositionSelect(HistoryDealGetString(HistoryDealGetTicket(HistoryDealsTotal()-1),DEAL_SYMBOL)) 
                  && (PositionGetDouble(POSITION_VOLUME) > HistoryDealGetDouble(HistoryDealGetTicket(HistoryDealsTotal()-1),DEAL_VOLUME)))
                  {
                     Alert("Buy position has incremented on pair ", 
                           HistoryDealGetString(HistoryDealGetTicket(HistoryDealsTotal()-1),DEAL_SYMBOL)); 
                  }
               break;
               
               case 1:
               // If volumes of position and deal are equal, then position has just been opened
                  if (PositionSelect(HistoryDealGetString(HistoryDealGetTicket(HistoryDealsTotal()-1),DEAL_SYMBOL)) 
                  && (PositionGetDouble(POSITION_VOLUME) == HistoryDealGetDouble(HistoryDealGetTicket(HistoryDealsTotal()-1),DEAL_VOLUME)))
                  {
                     Alert("Sell position has been opened on pair ", 
                           HistoryDealGetString(HistoryDealGetTicket(HistoryDealsTotal()-1),DEAL_SYMBOL)); 
                  }
                  else
               // If volumes of position and deal are not equal, then position has been incremented
                  if (PositionSelect(HistoryDealGetString(HistoryDealGetTicket(HistoryDealsTotal()-1),DEAL_SYMBOL)) 
                  && (PositionGetDouble(POSITION_VOLUME) > HistoryDealGetDouble(HistoryDealGetTicket(HistoryDealsTotal()-1),DEAL_VOLUME)))
                  {
                     Alert("Sell position has incremented on pair ", 
                           HistoryDealGetString(HistoryDealGetTicket(HistoryDealsTotal()-1),DEAL_SYMBOL)); 
                  }
                  
               break;
               
               default:
                  Alert("Unprocessed code of type: ",
                        HistoryDealGetInteger(HistoryDealGetTicket(HistoryDealsTotal()-1),DEAL_TYPE));
               break;
            }
         break;
         
         // Exiting the market
         case DEAL_ENTRY_OUT:
         Alert(HistoryDealGetInteger(HistoryDealGetTicket(HistoryDealsTotal()-1),DEAL_ORDER), 
         " order invoked deal #",HistoryDealGetTicket(HistoryDealsTotal()-1));
         
            switch(HistoryDealGetInteger(HistoryDealGetTicket(HistoryDealsTotal()-1),DEAL_TYPE))
            {
               case 0:
               // If position, we tried to close, is still present, then we have closed only part of it
                  if (PositionSelect(HistoryDealGetString(HistoryDealGetTicket(HistoryDealsTotal()-1),DEAL_SYMBOL)) == true)
                  {
                     Alert("Part of Sell position has been closed on pair ", 
                           HistoryDealGetString(HistoryDealGetTicket(HistoryDealsTotal()-1),DEAL_SYMBOL),
                           " with profit = ",
                           HistoryDealGetDouble(HistoryDealGetTicket(HistoryDealsTotal()-1),DEAL_PROFIT));
                  }
                  else
               // If position is not found, then it is fully closed
                  if (PositionSelect(HistoryDealGetString(HistoryDealGetTicket(HistoryDealsTotal()-1),DEAL_SYMBOL)) == false)
                  {
                     Alert("Sell position has been closed on pair ",
                           HistoryDealGetString(HistoryDealGetTicket(HistoryDealsTotal()-1),DEAL_SYMBOL),
                           " with profit = ",
                           HistoryDealGetDouble(HistoryDealGetTicket(HistoryDealsTotal()-1),DEAL_PROFIT));
                  }
               break;
               
               case 1:
               // If position, we tried to close, is still present, then we have closed only part of it
                  if (PositionSelect(HistoryDealGetString(HistoryDealGetTicket(HistoryDealsTotal()-1),DEAL_SYMBOL)) == true)
                  {
                     Alert("Part of Buy position has been closed on pair ", 
                           HistoryDealGetString(HistoryDealGetTicket(HistoryDealsTotal()-1),DEAL_SYMBOL),
                           " with profit = ",
                           HistoryDealGetDouble(HistoryDealGetTicket(HistoryDealsTotal()-1),DEAL_PROFIT));
                  }
                  else
               // If position is not found, then it is fully closed
                  if (PositionSelect(HistoryDealGetString(HistoryDealGetTicket(HistoryDealsTotal()-1),DEAL_SYMBOL)) == false)
                  {
                     Alert("Buy position has been closed on pair ",
                           HistoryDealGetString(HistoryDealGetTicket(HistoryDealsTotal()-1),DEAL_SYMBOL),
                           " with profit = ",
                           HistoryDealGetDouble(HistoryDealGetTicket(HistoryDealsTotal()-1),DEAL_PROFIT));
                  }
                  
               break;
               
               default:
                  Alert("Unprocessed code of type: ",
                        HistoryDealGetInteger(HistoryDealGetTicket(HistoryDealsTotal()-1),DEAL_TYPE));
               break;
            }
         break;
         
         // Reverse
         case DEAL_ENTRY_INOUT:
         Alert(HistoryDealGetInteger(HistoryDealGetTicket(HistoryDealsTotal()-1),DEAL_ORDER), 
         " order invoked deal #",HistoryDealGetTicket(HistoryDealsTotal()-1));
         
            switch(HistoryDealGetInteger(HistoryDealGetTicket(HistoryDealsTotal()-1),DEAL_TYPE))
            {
               case 0:
                  Alert("Sell is reversed to Buy on pair ",
                        HistoryDealGetString(HistoryDealGetTicket(HistoryDealsTotal()-1),DEAL_SYMBOL),
                        " resulting profit = ",
                        HistoryDealGetDouble(HistoryDealGetTicket(HistoryDealsTotal()-1),DEAL_PROFIT)); 
               break;
               
               case 1:
                  Alert("Buy is reversed to Sell on pair ",
                        HistoryDealGetString(HistoryDealGetTicket(HistoryDealsTotal()-1),DEAL_SYMBOL),
                        " resulting profit = ",
                        HistoryDealGetDouble(HistoryDealGetTicket(HistoryDealsTotal()-1),DEAL_PROFIT)); 
               break;
               
               default:
                  Alert("Unprocessed code of type: ",
                        HistoryDealGetInteger(HistoryDealGetTicket(HistoryDealsTotal()-1),DEAL_TYPE));
               break;
            }
         break;
         
         // Indicates the state record
         case DEAL_ENTRY_STATE:
            Alert("Indicates the state record. Unprocessed code of type: ", 
            HistoryDealGetInteger(HistoryDealGetTicket(HistoryDealsTotal()-1),DEAL_TYPE));
         break;
      }
      // --
   }
}

OrdersPrev = OrdersTotal();

//---
  }

Ayrıca programın başında aşağıdaki değişkenleri bildirdiğinizden emin olun:

datetime start_date = 0;   // Date, from which we begin to read history

int OrdersPrev = 0;        // Number of orders at the time of previous OnTrade() call
int PositionsPrev = 0;     // Number of positions at the time of previous OnTrade() call
ulong LastOrderTicket = 0; // Ticket of the last processed order

int _GetLastError=0;       // Error code
long state=0;              // Order state

OnTrade() çağrısının içeriğine geri dönelim.

Uyarıyı başlangıçta yorumlayabilirsiniz, ancak ben bırakacağım, Sonraki HistorySelect() fonksiyonuna gider. Fonksiyonun iki parametresi tarafından tanımlanan, belirtilen süre için bir sözleşme ve emir geçmişi listesi oluşturur. Sözleşme ve emir geçmişine gitmeden önce bu fonksiyon çağrılmazsa, geçmiş listeleri boş olacağı için herhangi bir bilgi alamayız. HistorySelect() çağrıldıktan sonra, daha önce yazıldığı gibi koşullar değerlendirilir.

Yeni emir geldiğinde önce bunu seçip hataları kontrol ediyoruz:

OrderGetTicket(OrdersTotal()-1);// Select the last order for work
_GetLastError=GetLastError();
Print("Error #",_GetLastError);ResetLastError();

Emri seçtikten sonra GetLastError() fonksiyonunu kullanarak hata kodunu alıyoruz. Sonra Print() fonksiyonunu kullanarak kodu günlüğe yazdırırız ve ResetLastError() fonksiyonunu kullanarak hata kodunu sıfıra sıfırlarız, böylece diğer durumlar için bir sonraki GetLastError() çağrısında aynı hata kodunu almayacağız.

Hataları kontrol ettikten sonra, emir başarıyla seçildiyse durumunu kontrol edin:

if (OrderGetInteger(ORDER_STATE) == ORDER_STATE_STARTED)
{
   Alert(OrderGetTicket(OrdersTotal()-1),"Order has arrived for processing");
   LastOrderTicket = OrderGetTicket(OrdersTotal()-1);    // Saving the order ticket for further work
}

Emir başlatıldı durumundaysa, yani doğruluğu kontrol edilmiş ancak henüz kabul edilmemişse, o zaman yakın bir zamanda yürütülmesi beklenir ve biz sadece emrin işlenmekte olduğunu bildiren bir Alert() veririz ve sonraki OnTrade() çağrısında biletini kaydederiz. Alert() yerine başka türde bildirimleri kullanabilirsiniz.

Yukarıdaki kodda

OrderGetTicket(OrdersTotal()-1)

tüm emir listesinden son emir biletini döndürür.

OrdersTotal()-1, en son emri almamız gerektiğini belirtir. OrdersTotal() fonksiyonu toplam emir sayısını verdiği için (örneğin, listede 1 emir varsa, OrdersTotal() 1 değerini verir) ve emir dizin numarası 0'dan sayılır, ardından son emrin dizin numarasını elde etmek için toplam emir sayısından 1 çıkarmalıyız (OrdersTotal() 1 verirse, bu emrin indeks numarası 0'a eşit olacaktır). Ve OrderGetTicket() fonksiyonu sırası geldiğinde kendisine numaranın iletileceği emir biletini verir.

Bu, ilk koşuldu, genellikle ilk OnTrade() çağrısında tetiklenir. Ardından, OnTrade() çağrısında karşılanan ikinci koşul gelir, emir yürütüldüğünde geçmişe iner ve pozisyon açılmalıdır.

Emir eksikse, geçmişe geçtiyse, kesinlikle burada olmalıdır! Bu nedenle, emir durumunu almak için HistoryOrderGetInteger() fonksiyonunu kullanarak emir geçmişine başvururuz. Ve belirli bir emrin geçmiş verilerini okumak için biletine ihtiyacımız vardır. Bunun için ilk koşulsa gelen emrin bileti LastOrderTicket değişkeninde depolanmıştır.

Böylece, HistoryOrderGetInteger() için birinci parametre olarak emir biletini ve ikinci olarak gerekli özelliğin türünü belirten emir durumunu elde ederiz. Emir durumunu almaya çalıştıktan sonra hata kodunu alıp günlüğe yazıyoruz. Çalışmamız gereken emriniz henüz geçmişe geçmediyse ve buna hitap etmemiz durumunda gereklidir (deneyimler bunun mümkün olduğunu ve oldukça fazla olduğunu göstermektedir. Bu makalenin başında bunun hakkında yazdım).

Bir hata oluşursa, çalışılacak veri olmadığından ve aşağıdaki koşullardan hiçbiri karşılanmadığından işleme durur. Ve HistoryOrderGetInteger() çağrısı başarılıysa ve emir "Emir tamamen yürütüldü" durumundaysa:

// If order is fully executed
if (state == ORDER_STATE_FILLED)

Ardından başka bir bildirimde bulunun:

// Then analyze the last deal
// --
  Alert(LastOrderTicket, "Order executed, going to deal");

Ve bu emir tarafından başlatılan sözleşmeyi işleme koyalım. İlk önce sözleşmenin yönünü (DEAL_ENTRY özelliği) öğrenin. Yön Alış veya Satış değil, Piyasaya giriş, Piyasadan çıkış, Ters veya Durum kaydı göstergesidir. Böylece DEAL_ENTRY özelliğini kullanarak emrin açık pozisyona mı, kapalı pozisyona mı yoksa terse mi ayarlandığını öğrenebiliriz. 

Sözleşmeyi ve sonuçlarını analiz etmek için, aşağıdaki yapıyı kullanarak geçmişe de başvuralım:

switch(HistoryDealGetInteger(HistoryDealGetTicket(HistoryDealsTotal()-1),DEAL_ENTRY))
{
  ...
}

Bu, emirlerle aynı şekilde çalışır:

HistoryDealsTotal(), toplam sözleşme sayısını verir. En son sözleşmenin numarasını almak için HistoryDealsTotal() değerinden 1 çıkarırız. Elde edilen anlaşma sayısı, seçilen sözleşmenin biletini HistoryDealGetInteger() fonksiyonuna ileten HistoryDealGetTicket() fonksiyonuna iletilir. Ve belirtilen bilet ve özellik türüne göre HistoryDealGetInteger(), sözleşmenin yönünü verecektir.

Piyasaya giriş yönünü detaylı olarak inceleyelim. Diğer yönler, hemen hemen aynı şekilde işlendikleri için kısaca ele alınacaktır:

HistoryDealGetInteger()'den elde edilen ifadenin değeri, bir eşleşme bulunana kadar durum bloklarının değerleriyle karşılaştırılır. Piyasaya girdiğimizi, yani Satış emrini açtığımızı varsayalım. Ardından birinci blok yürütülecektir:

// Entering the market
case DEAL_ENTRY_IN:

Bloğun başında, sözleşmenin oluşturulması hakkında bilgilendirilirsiniz. Aynı zamanda bu bildirim, her şeyin yolunda olmasını ve sözleşmenin işleme koyulmasını sağlar.

Bildirimden sonra, sözleşmenin türünü analiz eden başka bir anahtar bloğu gelir:

   switch(HistoryDealGetInteger(HistoryDealGetTicket(HistoryDealsTotal()-1),DEAL_TYPE))
   {
      case 0:
      // If volumes of position and deal are equal, then position has just been opened
         if (PositionSelect(HistoryDealGetString(HistoryDealGetTicket(HistoryDealsTotal()-1),DEAL_SYMBOL)) 
         && (PositionGetDouble(POSITION_VOLUME) == HistoryDealGetDouble(HistoryDealGetTicket(HistoryDealsTotal()-1),DEAL_VOLUME)))
         {
            Alert("Buy position has been opened on pair ", 
                  HistoryDealGetString(HistoryDealGetTicket(HistoryDealsTotal()-1),DEAL_SYMBOL)); 
         }
         else
      // If volumes of position and deal are not equal, then position has been incremented
         if (PositionSelect(HistoryDealGetString(HistoryDealGetTicket(HistoryDealsTotal()-1),DEAL_SYMBOL)) 
         && (PositionGetDouble(POSITION_VOLUME) > HistoryDealGetDouble(HistoryDealGetTicket(HistoryDealsTotal()-1),DEAL_VOLUME)))
         {
            Alert("Buy position has incremented on pair ", 
                  HistoryDealGetString(HistoryDealGetTicket(HistoryDealsTotal()-1),DEAL_SYMBOL)); 
         }
      break;
      
      case 1:
      // If volumes of position and deal are equal, then position has just been opened
         if (PositionSelect(HistoryDealGetString(HistoryDealGetTicket(HistoryDealsTotal()-1),DEAL_SYMBOL)) 
         && (PositionGetDouble(POSITION_VOLUME) == HistoryDealGetDouble(HistoryDealGetTicket(HistoryDealsTotal()-1),DEAL_VOLUME)))
         {
            Alert("Sell position has been opened on pair ", 
                  HistoryDealGetString(HistoryDealGetTicket(HistoryDealsTotal()-1),DEAL_SYMBOL)); 
         }
         else
      // If volumes of position and deal are not equal, then position has been incremented
         if (PositionSelect(HistoryDealGetString(HistoryDealGetTicket(HistoryDealsTotal()-1),DEAL_SYMBOL)) 
         && (PositionGetDouble(POSITION_VOLUME) > HistoryDealGetDouble(HistoryDealGetTicket(HistoryDealsTotal()-1),DEAL_VOLUME)))
         {
            Alert("Sell position has incremented on pair ", 
                  HistoryDealGetString(HistoryDealGetTicket(HistoryDealsTotal()-1),DEAL_SYMBOL)); 
         }
         
      break;
      
      default:
         Alert("Unprocessed code of type: ",
               HistoryDealGetInteger(HistoryDealGetTicket(HistoryDealsTotal()-1),DEAL_TYPE));
      break;
   }

Sözleşme hakkında geçmişten bilgi alın - belirtilen özellik dışında daha önce olduğu gibi. Bu sefer Alış veya Satış sözleşmesi yapılıp yapılmadığını bilmek için DEAL_TYPE öğesini belirtmelisiniz. Sadece Alış ve Satış türlerini analiz ediyorum, ancak bunların yanında dört tane daha var. Ancak bu kalan dört tür sözleşme daha az yaygındır, bu nedenle dört durum bloğu yerine bunlar için yalnızca bir varsayılan blok kullanılır. Tür koduyla birlikte bir Alert() verecektir.

Muhtemelen kodda fark ettiğiniz gibi, sadece Alış ve Satış pozisyonlarının açılması değil, aynı zamanda artışları da işlenir. Pozisyonun ne zaman artırıldığını ve ne zaman açıldığını belirlemek için, gerçekleştirilen sözleşme hacmini ve bu sözleşmenin sonucu olan pozisyonu karşılaştırmanız gerekir. Pozisyon hacmi gerçekleştirilen sözleşme hacmine eşitse, bu pozisyon açılmıştır ve pozisyon ve sözleşme hacimleri farklıysa, bu pozisyon artırılmıştır. Bu, hem Alış pozisyonları ('0' bloğu durumunda) hem de Satış pozisyonları ('1' bloğu durumunda) için geçerlidir. Son blok, Alış ve Satış dışındaki tüm durumları ele alan varsayılandır. İşlemin tamamı, HistoryDealGetInteger() fonksiyonu tarafından verilen tür koduyla ilgili bildirimden oluşur.

Ve son olarak, son sorun pozisyonlarla çalışma ile ilgilidir. Bu, Zararı Durdur ve Kar Al değerlerindeki değişikliklerin işlenmesidir. Pozisyon parametrelerinden hangisinin değiştiğini bilmek için, parametrelerinin mevcut ve önceki durumunu karşılaştırmamız gerekir. Pozisyon parametrelerinin mevcut değerleri her zaman servis fonksiyonları kullanılarak elde edilebilir, ancak önceki değerler kaydedilmelidir.

Bunun için pozisyon parametrelerini yapı dizisine kaydedecek özel bir fonksiyon yazacağız:

void GetPosition(_position &Array[])
  {
   int _GetLastError=0,_PositionsTotal=PositionsTotal();

   int temp_value=(int)MathMax(_PositionsTotal,1);
   ArrayResize(Array, temp_value);

   _ExpertPositionsTotal=0;
   for(int z=_PositionsTotal-1; z>=0; z--)
     {
      if(!PositionSelect(PositionGetSymbol(z)))
        {
         _GetLastError=GetLastError();
         Print("OrderSelect() - Error #",_GetLastError);
         continue;
        }
      else
        {
            // If the position is found, then put its info to the array
            Array[z].type         = PositionGetInteger(POSITION_TYPE);
            Array[z].time         = PositionGetInteger(POSITION_TIME);
            Array[z].magic        = PositionGetInteger(POSITION_MAGIC);
            Array[z].volume       = PositionGetDouble(POSITION_VOLUME);
            Array[z].priceopen    = PositionGetDouble(POSITION_PRICE_OPEN);
            Array[z].sl           = PositionGetDouble(POSITION_SL);
            Array[z].tp           = PositionGetDouble(POSITION_TP);
            Array[z].pricecurrent = PositionGetDouble(POSITION_PRICE_CURRENT);
            Array[z].comission    = PositionGetDouble(POSITION_COMMISSION);
            Array[z].swap         = PositionGetDouble(POSITION_SWAP);
            Array[z].profit       = PositionGetDouble(POSITION_PROFIT);
            Array[z].symbol       = PositionGetString(POSITION_SYMBOL);
            Array[z].comment      = PositionGetString(POSITION_COMMENT);
        _ExpertPositionsTotal++;
        }
     }

   temp_value=(int)MathMax(_ExpertPositionsTotal,1);
   ArrayResize(Array,temp_value);
  }

Bu fonksiyonu kullanmak için global değişkenler bildirim bloğuna aşağıdaki kodu eklemeliyiz:

/*
 *
 * Structure that stores information about positions
 *
 */
struct _position
{

long     type,          // Position type
         magic;         // Magic number for position
datetime time;          // Time of position opening

double   volume,        // Position volume
         priceopen,     // Position price
         sl,            // Stop Loss level for opened position
         tp,            // Take Profit level for opened position
         pricecurrent,  // Symbol current price
         comission,     // Commission
         swap,          // Accumulated swap
         profit;        // Current profit

string   symbol,        // Symbol, by which the position has been opened
         comment;       // Comment to position
};

int _ExpertPositionsTotal = 0;

_position PositionList[],     // Array that stores info about position
          PrevPositionList[];

GetPosition() fonksiyonu prototipi uzun zaman önce www.mql4.com makalelerinde bulunuyordu, ancak şimdi bulamadım ve kaynak belirtemiyorum. Bu fonksiyonun çalışmasını ayrıntılı olarak tartışmayacağım. Mesele şu ki, referans yoluyla bir parametre olarak, mevcut zamanda açık olan pozisyonlar ve parametrelerinin değerleri hakkındaki tüm bilgilerin iletildiği _position türünde (pozisyon alanlarına karşılık gelen alanlara sahip yapı) bir dizi geçirilir.

Pozisyon parametrelerindeki değişiklikleri uygun şekilde izlemek için _position türünde iki dizi oluşturalım. Bunlar PositionList[] (pozisyonların mevcut durumu) ve PrevPositionList[] (pozisyonların önceki durumu) dizileridir.

Pozisyonlarla çalışmaya başlamak için, sonraki çağrıyı OnInit() ve OnTrade() çağrısının sonuna eklemeliyiz:

GetPosition(PrevPositionList);

Ayrıca Ontrade() çağrısının başına şu çağrıyı eklemeliyiz:

GetPosition(PositionList);

Şimdi PositionList[] ve PrevPositionList[] dizilerinde sırasıyla mevcut ve önceki OnTrade() çağrısındaki pozisyonlar hakkında bilgi elimizde olacak.

Şimdi sl ve tp'deki gerçek izleme değişiklikleri kodunu ele alalım:

if ((PositionsPrev == PositionsTotal()) && (OrdersPrev == OrdersTotal()))
{
   string _alerts = "";
   bool modify = false;
     
   for (int i=0;i<_ExpertPositionsTotal;i++)
   {
      if (PrevPositionList[i].sl != PositionList[i].sl)
      {
         _alerts += "On pair "+PositionList[i].symbol+" Stop Loss changed from "+ PrevPositionList[i].sl +" to "+ PositionList[i].sl +"\n";
         modify = true;
      }
      if (PrevPositionList[i].tp != PositionList[i].tp)
      {
         _alerts += "On pair "+PositionList[i].symbol+" Take Profit changed from "+ PrevPositionList[i].tp +" to "+ PositionList[i].tp +"\n";
         modify = true;
      }
      
   }
   if (modify == true)
   {
      Alert(_alerts);
      modify = false;
   }
}

Gördüğümüz gibi, kod çok büyük değil, ancak bu yalnızca önemli hazırlık çalışmaları nedeniyledir. Bunu inceleyelim.

Her şey şu koşulla başlar:

if ((PositionsPrev == PositionsTotal()) && (OrdersPrev == OrdersTotal()))

Burada ne emirlerin ne de pozisyonların verilmediğini veya silinmediğini görüyoruz. Koşul karşılanırsa, büyük olasılıkla bazı pozisyonların veya emirlerin parametreleri değişmiştir.

Fonksiyonun başında iki değişken bildirilir:

  • _alerts - değişikliklerle ilgili tüm bildirimleri depolar.
  • modify - değişikliklerle ilgili mesajları yalnızca gerçekten yapılmışlarsa görüntülemenize olanak tanır.

Döngünün bir sonraki adımında, her pozisyon için önceki ve mevcut OnTrade() çağrısında Kar Al ve Zararı Durdur değerlerinin eşleşmesini kontrol ederiz. Tüm uyumsuzluklarla ilgili bilgiler _alerts değişkeninde depolanacak ve daha sonra Alert() fonksiyonu tarafından görüntülenecektir. Bu arada, bekleyen emir değişikliklerinin işlenmesi de aynı şekilde gerçekleştirilecektir.

Şimdilik pozisyonları bitirelim ve bekleyen emirlerin yerleştirilmesine geçelim.

4. Emirlerle çalışma

Bekleyen emirler etkinliğinin yerleştirilmesiyle başlayalım.

Bekleyen yeni emir göründüğünde, Alım Satım etkinliği yalnızca bir kez oluşturulur, ancak bunu işlemek yeterlidir! Bekleyen emirlerle çalışan kodu operatörün gövdesine yerleştirin:

if (OrdersPrev < OrdersTotal())

Ve aşağıdakileri alın:

if (OrdersPrev < OrdersTotal())
{
   OrderGetTicket(OrdersTotal()-1);// Select the last order to work with
   _GetLastError=GetLastError();
   Print("Error #",_GetLastError);ResetLastError();
   //--
   if (OrderGetInteger(ORDER_STATE) == ORDER_STATE_STARTED)
   {
      Alert(OrderGetTicket(OrdersTotal()-1),"Order has arrived for processing");
      LastOrderTicket = OrderGetTicket(OrdersTotal()-1);    // Saving the order ticket for further work
   }
   
   state = OrderGetInteger(ORDER_STATE);
   if (state == ORDER_STATE_PLACED)
   {
      switch(OrderGetInteger(ORDER_TYPE))
      {
         case 2:
            Alert("Pending order Buy Limit #", OrderGetTicket(OrdersTotal()-1)," accepted!");
         break;
         
         case 3:
            Alert("Pending order Sell Limit #", OrderGetTicket(OrdersTotal()-1)," accepted!");
         break;
         
         case 4:
            Alert("Pending order Buy Stop #", OrderGetTicket(OrdersTotal()-1)," accepted!");
         break;
         
         case 5:
            Alert("Pending order Sell Stop #", OrderGetTicket(OrdersTotal()-1)," accepted!");
         break;
         
         case 6:
            Alert("Pending order Buy Stop Limit #", OrderGetTicket(OrdersTotal()-1)," accepted!");
         break;
                 
         case 7:
            Alert("Pending order Sell Stop Limit  #", OrderGetTicket(OrdersTotal()-1)," accepted!");
         break;         
      }
   }
}

Burada bekleyen emirlerle çalışan kod aşağıdakilerle başlar:

   state = OrderGetInteger(ORDER_STATE);
   if (state == ORDER_STATE_PLACED)
   {

Önce emir durumu kontrol edilir. Emir, ORDER_STATE_PLACED durumunda olmalıdır, yani kabul edilmelidir. Ve bu koşul sağlanırsa, emir türüne bağlı olarak bir mesaj yazdıran anahtar operatörü gelir.

Daha sonra, emirler değiştirildiğinde meydana gelen etkinliklerle çalışacağız. Emirlerin değiştirilmesi, pozisyonların değiştirilmesine benzer. Benzer şekilde, emir özelliklerini depolayan yapı oluşturulur: 

/*
 *
 * Structure that stores information about orders
 *
 */
struct _orders
{

datetime time_setup,       // Time of order placement
         time_expiration,  // Time of order expiration
         time_done;        // Time of order execution or cancellation
         
long     type,             // Order type
         state,            // Order state
         type_filling,     // Type of execution by remainder
         type_time,        // Order lifetime
         ticket;           // Order ticket
         
long     magic,            // Id of Expert Advisor, that placed an order
                           // (intended to ensure that each Expert 
                           // must place it's own unique number)
                           
         position_id;      // Position id, that is placed on order,
                           // when it is executed. Each executed order invokes a
                           // deal, that opens new or changes existing 
                           // position. Id of that position is placed on 
                           // executed order in this moment.
                           
double volume_initial,     // Initial volume on order placement
       volume_current,     // Unfilled volume
       price_open,         // Price, specified in the order
       sl,                 // Stop Loss level
       tp,                 // Take Profit level
       price_current,      // Current price by order symbol
       price_stoplimit;    // Price of placing Limit order when StopLimit order is triggered
       
string symbol,             // Symbol, by which the order has been placed
       comment;            // Comment
                           
};

int _ExpertOrdersTotal = 0;

_orders OrderList[],       // Arrays that store info about orders
        PrevOrderList[];

Yapının her alanı, emir özelliklerinden birine karşılık gelir. Yapıyı bildirdikten sonra, int türündeki değişken ve _orders türündeki iki dizi bildirilir. _ExpertOrdersTotal değişkeni toplam emir sayısını depolayacak ve OrderList[] ve PrevOrderList[] dizileri sırasıyla mevcut ve önceki OnTrade() çağrısındaki emirlerle ilgili bilgileri depolayacaktır.

Fonksiyonun kendisi aşağıdaki gibi görünecektir:

void GetOrders(_orders &OrdersList[])
  {
   
   int _GetLastError=0,_OrdersTotal=OrdersTotal();

   int temp_value=(int)MathMax(_OrdersTotal,1);
   ArrayResize(OrdersList,temp_value);

   _ExpertOrdersTotal=0;
   for(int z=_OrdersTotal-1; z>=0; z--)
     {
      if(!OrderGetTicket(z))
        {
         _GetLastError=GetLastError();
         Print("GetOrders() - Error #",_GetLastError);
         continue;
        }
      else
        {
        OrdersList[z].ticket          = OrderGetTicket(z);
        OrdersList[z].time_setup      = OrderGetInteger(ORDER_TIME_SETUP);
        OrdersList[z].time_expiration = OrderGetInteger(ORDER_TIME_EXPIRATION);
        OrdersList[z].time_done       = OrderGetInteger(ORDER_TIME_DONE);
        OrdersList[z].type            = OrderGetInteger(ORDER_TYPE);
        
        OrdersList[z].state           = OrderGetInteger(ORDER_STATE);
        OrdersList[z].type_filling    = OrderGetInteger(ORDER_TYPE_FILLING);
        OrdersList[z].type_time       = OrderGetInteger(ORDER_TYPE_TIME);
        OrdersList[z].magic           = OrderGetInteger(ORDER_MAGIC);
        OrdersList[z].position_id     = OrderGetInteger(ORDER_POSITION_ID);
        
        OrdersList[z].volume_initial  = OrderGetDouble(ORDER_VOLUME_INITIAL);
        OrdersList[z].volume_current  = OrderGetDouble(ORDER_VOLUME_CURRENT);
        OrdersList[z].price_open      = OrderGetDouble(ORDER_PRICE_OPEN);
        OrdersList[z].sl              = OrderGetDouble(ORDER_SL);
        OrdersList[z].tp              = OrderGetDouble(ORDER_TP);
        OrdersList[z].price_current   = OrderGetDouble(ORDER_PRICE_CURRENT);
        OrdersList[z].price_stoplimit = OrderGetDouble(ORDER_PRICE_STOPLIMIT);
        
        OrdersList[z].symbol          = OrderGetString(ORDER_SYMBOL);
        OrdersList[z].comment         = OrderGetString(ORDER_COMMENT);
        
        _ExpertOrdersTotal++;
        }
     }

   temp_value=(int)MathMax(_ExpertOrdersTotal,1);
   ArrayResize(OrdersList,temp_value);

  }

GetPosition() fonksiyonuna benzer şekilde, verilen her emrin özellikleri hakkındaki bilgileri okur ve bunu giriş parametresi olarak iletilen diziye koyar. Fonksiyon kodu, uzmanınızın ve çağrılarının sonuna aşağıdaki gibi yerleştirilmelidir:

GetOrders(PrevOrderList);

OnInit() içine ve OnTrade() çağrısının sonuna yerleştirilir.

GetOrders(OrderList);

OnTrade() çağrısının başına yerleştirilir.

Şimdi emirlerin değiştirilmesini işleyecek kodu göz önünde bulundurun. Bu bir döngüdür ve pozisyon değişikliği kodunu tamamlar:

   for (int i = 0;i<_ExpertOrdersTotal;i++)
   {
      if (PrevOrderList[i].sl != OrderList[i].sl)
      {
         _alerts += "Order "+OrderList[i].ticket+" has changed Stop Loss from "+ PrevOrderList[i].sl +" to "+ OrderList[i].sl +"\n";
         modify = true;
      }
      if (PrevOrderList[i].tp != OrderList[i].tp)
      {
         _alerts += "Order "+OrderList[i].ticket+" has changed Take Profit from "+ PrevOrderList[i].tp +" to "+ OrderList[i].tp +"\n";
         modify = true;
      }
   }

Döngü tüm emirleri işler ve mevcut ve önceki OnTrade() çağrılarındaki Zararı Durdur ve Kar Al değerlerini karşılaştırır. Farklılıklar varsa, bunlar _alerts değişkenine kaydedilir ve döngü tamamlandığında Alert() fonksiyonu tarafından görüntülenecektir.

Bu kod operatörün gövdesine yerleştirilir:

if ((PositionsPrev == PositionsTotal()) && (OrdersPrev == OrdersTotal()))
{

Döngüden hemen sonra pozisyonlarla çalışır.

Şimdilik alım satım etkinlikleriyle ilgili çalışmalar bitmiyor. Bu makale yalnızca Alım Satım etkinliğiyle çalışmanın ana ilkelerini kapsar. Genel olarak, bu yöntemin sunduğu olanaklar oldukça büyüktür ve bu makalenin kapsamını aşar.

Sonuç

Alım satım etkinlikleriyle çalışma becerisi (MQL5 dilinin bir parçası olarak) potansiyel olarak güçlü bir araçtır; bu, yalnızca nispeten hızlı bir şekilde emir doğrulama algoritmalarını uygulamaya ve alım satım raporları oluşturmaya değil, aynı zamanda sistem kaynaklarının maliyetini ve kaynak kodunun hacmini azaltmaya olanak tanır, bu, geliştiriciler için şüphesiz fayda sağlayacaktır.

MetaQuotes Ltd tarafından Rusçadan çevrilmiştir.
Orijinal makale: https://www.mql5.com/ru/articles/40

Ekli dosyalar |
tradecontrol_en.mq5 (20.25 KB)
MQL5'te göstergeler nasıl çağrılır MQL5'te göstergeler nasıl çağrılır
MQL programlama dilinin yeni versiyonuyla birlikte, yalnızca göstergelerle ilgilenme yaklaşımı değişmedi, aynı zamanda göstergelerin nasıl oluşturulacağının da yeni yolları var. Ayrıca, göstergenin tamponlarıyla çalışma esnekliğine sahipsiniz - artık istediğiniz indeksleme yönünü belirleyebilir ve tam olarak istediğiniz kadar gösterge değeri alabilirsiniz. Bu makale, göstergeleri çağırmanın ve göstergenin tamponundan veri almanın temel yöntemlerini açıklar.
Yeni Başlayanlar için MQL5'te Özel Göstergeler Yeni Başlayanlar için MQL5'te Özel Göstergeler
Herhangi bir yeni konu, bir acemi için karmaşık ve öğrenmesi zor görünür. Bildiğimiz konular ise bize çok basit ve anlaşılır gelir. Ancak, herkesin bir şeyi sıfırdan ve hatta ana dilimizden öğrenmek zorunda olduğunu hatırlamıyoruz. Aynısı, kişinin kendi alım satım stratejilerini geliştirmesi için geniş olanaklar sunan MQL5 programlama dili için de geçerlidir - bunu temel kavramlardan ve en basit örneklerden öğrenmeye başlayabilirsiniz. Teknik bir göstergenin MetaTrader 5 istemci terminali ile etkileşimi, bu makalede basit özel gösterge SMA örneğinde ele alınmaktadır.
MQL5'teki Çizim Stilleri MQL5'teki Çizim Stilleri
MQL4'te 6 çizim stili ve MQL5'te 18 çizim stili vardır. Bu nedenle, MQL5'in çizim stillerini tanıtmak için bir makale yazmaya değer olabilir. Bu yazıda MQL5'teki çizim stillerinin detaylarını ele alacağız. Ek olarak, bu çizim stillerinin nasıl kullanılacağını göstermek için bir gösterge oluşturacağız ve çizimi iyileştireceğiz.
Nesne işaretleyicilerini MQL5'te Kullanma Nesne işaretleyicilerini MQL5'te Kullanma
Varsayılan olarak, MQL5'teki tüm nesneler referansla iletilir, ancak nesne işaretçilerini kullanma olasılığı vardır. Ancak, nesne başlatılmamış olabileceğinden, işaretçi denetiminin gerçekleştirilmesi gereklidir. Bu durumda MQL5 programı kritik bir hata ile sonlandırılır ve kaldırılır. Otomatik olarak oluşturulan nesneler böyle bir hataya neden olmaz, dolayısıyla bu anlamda oldukça güvenlidirler. Bu makalede, nesne referansı ile nesne işaretçisi arasındaki farkı anlamaya çalışacağız ve işaretçileri kullanan güvenli kodun nasıl yazılacağını ele alacağız.