English Русский 中文 Español Deutsch 日本語 Português 한국어 Français Italiano
Standart Kitaplık Sınıflarını ve Google Grafik API (Uygulama Programlama Arayüzü) kullanarak Bilgi Panosu oluşturmak

Standart Kitaplık Sınıflarını ve Google Grafik API (Uygulama Programlama Arayüzü) kullanarak Bilgi Panosu oluşturmak

MetaTrader 5Örnekler | 9 Aralık 2021, 10:49
65 0
Евгений
Евгений


Giriş

MQL5 dilinin programlayıcıları için her şeyi daha kolaylaştırmak amacıyla tasarımcılar, neredeyse tüm API MQL5 fonksiyonlarını kapsayan ve onlarla çalışmayı çok daha kolay ve rahat hale getiren Standart Kitaplık oluşturdu. Bu makale, standart kitaplık tarafından kullanılan maksimum sınıf sayısıyla bir bilgi panosu oluşturmaya odaklanacaktır.


1. Standart Kitaplık sınıflarına genel bakış

Öyleyse, kitaplık tam olarak nedir? Web sitesinin belgeleme bölümü şunlardan oluştuğunu bildirir:

Tüm sınıfların kodları da dahil olmak üzere dosyalar, MQL5/Include klasöründedir. Kitaplık kodunu izlerken sadece sınıfları sunduğunu, fonksiyonları ise sunmadığını fark edeceksiniz. Bu yüzden onu kullanmak için nesne yönelimli programlama (OOP) bilginiz olmalıdır. 

Tüm kitaplık sınıfları (alım satım sınıfları hariç) CObject temel sınıfından gelir. Bunu göstermek için Sınıfı diyagram oluşturmaya çalışacağız ve bunun için gereken her şeye, yani temel sınıf ve türevlerine sahibiz. MQL5 dili temelde C++’ın bir alt seti olduğundan, diyagramın otomatik yapılandırılması amacıyla C++ projelerinin tersine mühendisliği için araçlar sunan IBM Rational Rose enstrümanını kullanalım.

 

Şekil 1. Standart Kitaplık sınıflarının diyagramı

Oluşturacağımız külfetli diyagramlardan dolayı sınıfların özelliklerini ve yöntemlerini göstereceğiz. Ayrıca bizim için önemli olmadığından kümelenmeleri dahil etmeyeceğiz. Sonuç olarak elimizde sadece genellemeler (kalıtımlar) var ve sınıfların edindiği özellikleri ve yöntemleri bulmamızı sağlayacaklar.

Diyagramdan görülebildiği gibi satırlar, dosyalar, grafikler, grafik nesneleri ve diziler ile çalışan her kitaplık bileşeninin, CObject’ten kalmış olan kendi temel sınıfı vardır (Sırasıyla CString, CFile, CChart, CChartObject ve CArray). CIndicator ve onun yardımcı CIndicators sınıfı göstergeleriyle çalışmak için gereken temel sınıf, CArrayObj öğesinden alınır, CIndicatorBuffer gösterge tamponu sınıfına erişim ise CArrayDouble öğesinden alınır.

Diyagramdaki koyu kırmızı renk, gerçeklik sınıfları, göstergeler, diziler ve ChartObjects içinde var olmayanları belirtir, bunlar setlerdir ve göstergeler, diziler ve grafik nesneleri ile çalışma sınıflarını içerir. Bunlar çok sayıda olduğundan ve tek bir üst öğeden devralındıklarından diyagramın aşırı dolmasını önlemek için biraz basitleştirmelere imkan tanıdım. Örneğin Gösterge CiDEMA, CiStdDev vs. içerir.

Ayrıca sınıf diyagramının, Doxygen belgeleme sisteminin otomatik oluşturulması kullanılarak yapılandırılabildiğini önemle belirtmek isterim. Bunu, Rational Rose yerine bu sistemde yapmak biraz daha kolaydır. Doxygen hakkında daha fazla bilgi, MQL5 Kodu için Otomatik Oluşturulan Belgeler makalesinde bulunabilir.


2. Sorun

Maksimum Standart Kitaplık sınıfları sayısıyla birlikte bir bilgi tablosu oluşturmayı deneyelim.

Pano neleri gösterecek? MetaTrader 5’in ayrıntılı bir raporu gibi, yani:

Şekil 2. Ayrıntılı raporun görünümü

Şekil 2. Ayrıntılı raporun görünümü

Gördüğümüz gibi rapor bir denge grafiğini ve bazı alım satım rakamlarını gösteriyor. Bu göstergelerin hesaplama yöntemleri hakkında daha fazla bilgiyi, Uzman Test Raporundaki Sayıların Anlamı makalesinde bulabilirsiniz.

Pano tamamen bilgi verme amaçlı olduğundan ve herhangi bir alım satım işlemi yapmadığından gerçek grafiğin kapanmasını önlemek için bunu ayrı bir pencerede bir gösterge olarak uygulamak en doğru seçim olacaktır. Ayrıca alt pencereye yerleştirilmesi, kolay ölçeklenmesini ve hatta panonun, tek bir tıklamayla kapanmasını sağlar.

Raporu, toplam işlem sayısına göre enstrümanda yapılan işlem sayısını gösteren dilim grafik ile tamamlamayı da isteyebilirsiniz.


3. Arayüzün tasarlanması 

Hedeflerimizi belirledik: Ana grafiğin alt penceresinde ayrıntılı bir rapora ihtiyacımız var.

Bilgi panomuzu bir sınıf olarak uygulayacağız. Haydi başlayalım:

//+------------------------------------------------------------------+
///The Board class
//+------------------------------------------------------------------+
class Board
  {
//protected data
protected:
///number of the sub-window where the board will be stored
   int               wnd;             
///array with the deals data   
   CArrayObj        *Data;
///array with the balance data   
   CArrayDouble      ChartData;       
///array with elements of the interface   
   CChartObjectEdit  cells[10][6];    
///object for working with the chart   
   CChart            Chart;           
///object for working with the balance chart
   CChartObjectBmpLabel BalanceChart; 
///object for working with the pie chart
   CChartObjectBmpLabel PieChart;     
///data for the pie chart
   PieData          *pie_data;
//private data and methods
private:
   double            net_profit;      //these variables will store the calculated characteristics
   double            gross_profit;
   double            gross_loss;
   double            profit_factor;
   double            expected_payoff;
   double            absolute_drawdown;
   double            maximal_drawdown;
   double            maximal_drawdown_pp;
   double            relative_drawdown;
   double            relative_drawdown_pp;
   int               total;
   int               short_positions;
   double            short_positions_won;
   int               long_positions;
   double            long_positions_won;
   int               profit_trades;
   double            profit_trades_pp;
   int               loss_trades;
   double            loss_trades_pp;
   double            largest_profit_trade;
   double            largest_loss_trade;
   double            average_profit_trade;
   double            average_loss_trade;
   int               maximum_consecutive_wins;
   double            maximum_consecutive_wins_usd;
   int               maximum_consecutive_losses;
   double            maximum_consecutive_losses_usd;
   int               maximum_consecutive_profit;
   double            maximum_consecutive_profit_usd;
   int               maximum_consecutive_loss;
   double            maximum_consecutive_loss_usd;
   int               average_consecutive_wins;
   int               average_consecutive_losses;

   ///method of obtaining data about the deals and the balance
   void              GetData();

   ///method of calculating the characteristics
   void              Calculate();
   
   ///method of chart construction
   void              GetChart(int X_size,int Y_size,string request,string file_name);
   
   ///method of request to Google Charts API
   string            CreateGoogleRequest(int X_size,int Y_size,bool type);
   
  ///method of obtaining the optimum font size
   int               GetFontSize(int x,int y);
   string            colors[12];  //array with text presentation of colors
//public methods
public:
///constructor
   void              Board();    
///destructor         
   void             ~Board();    
///method for board update
   void              Refresh();  
///method for creating interface elements   
   void              CreateInterface(); 
  };

Korunan sınıf verileri, arayüz öğeleri ve sözleşme, denge ve dilim grafik verileridir (PieData sınıfını aşağıda tartışacağız). Alım satım göstergeleri ve bazı yöntemler gizlidir. Bunlar gizlidir çünkü kullanıcının onlara doğrudan erişmemesi gereklidir, sınıfın içinde hesaplanırlar ve yalnızca uygun genel yöntemi çağırarak sayılabilirler.

Ayrıca arayüz oluşturma ve gösterge hesaplama yöntemleri de gizlidir, bu yüzden burada çok sıkı bir yöntem çağrıları sıralamasına dayanmanız gerekir. Örneğin göstergeleri hesaplama verisi olmadan hesaplamak veya arayüzü, önceden oluşturmadan güncellemek imkansızdır. Bu yüzden kullanıcının “bindiği dalı kesmesine” izin vermemeliyiz. 

Hemen bir sınıfın yapıcıları ve yıkıcıları ile ilgilenelim, böylece bunlara daha sonra geri dönmemiz gerekmeyecektir:

//+------------------------------------------------------------------+
///Constructor
//+------------------------------------------------------------------+
void Board::Board()
  {
   Chart.Attach();                               //attach the current chart to the class instance
   wnd=ChartWindowFind(Chart.ChartId(),"IT");    //find the indicator window
   Data = new CArrayObj;                         //creating the CArrayObj class instance
   pie_data=new PieData;                         //creating the PieData class instance
   //fill colors array
   colors[0]="003366"; colors[1]="00FF66"; colors[2]="990066";
   colors[3]="FFFF33"; colors[4]="FF0099"; colors[5]="CC00FF";
   colors[6]="990000"; colors[7]="3300CC"; colors[8]="000033";
   colors[9]="FFCCFF"; colors[10]="CC6633"; colors[11]="FF0000";
  }
//+------------------------------------------------------------------+
///Destructor
//+------------------------------------------------------------------+
void Board::~Board()
  {
   if(CheckPointer(Data)!=POINTER_INVALID) delete Data;   //delete the deals data
   if(CheckPointer(pie_data)!=POINTER_INVALID) delete pie_data;
   ChartData.Shutdown();    //and balance data
   Chart.Detach();          //detach from the chart
   for(int i=0;i<10;i++)    //delete all interface elements
      for(int j=0;j<6;j++)
         cells[i][j].Delete();
   BalanceChart.Delete();   //delete the balance chart
   PieChart.Delete();       //and pie chart
  }

Yapıcıda, Attach() yönteminin yardımıyla mevcut grafiğe bir CChart nesne türü bağlayacağız. Yıkıcıdaki Detach() yöntemi grafiğin nesneyle olan bağını kaldıracaktır. CArrayObj türü nesnenin işaretçisi olan veri nesnesi, nesnenin adresini aldı, yeni işlemi kullanarak dinamik olarak oluşturuldu ve sil işlemi kullanılarak kaldırıldı. Silmeden önce CheckPointer() öğesini kullanarak nesnenin varlığını kontrol etmeyi unutmayın, aksi takdirde bir hata oluşur.

CArrayObj sınıfı hakkında daha fazla bilgi daha sonra verilecektir. Diğer sınıfların CArray sınıfından devralındığı gibi CArrayDouble sınıfının Shutdown() yöntemi (sınıfların diyagramına bakın), nesne tarafından işgal edilen belleği temizler ve boşaltır. CChartObject sınıfı türevlerin Delete() yöntemi nesneyi grafikten kaldırır.

Böylece yapıcı, belleği tahsis eder ve yıkıcı onu serbest bırakır ve sınıf tarafından oluşturulan grafik nesnelerini kaldırır. 

Şimdi arayüzle ilgilenelim. Yukarıda belirtildiği gibi, CreateInterface() yöntemi panonun bir arabirimini oluşturur:

//+------------------------------------------------------------------+
///CreateInterface function
//+------------------------------------------------------------------+
void Board::CreateInterface()
  {
   //retrieve the width
   int x_size=Chart.WidthInPixels();
   //and the height of the indicator window
   int y_size=Chart.GetInteger(CHART_HEIGHT_IN_PIXELS,wnd);
   
    //calculate, how much space will the balance chart take up
   double chart_border=y_size*(1.0-(Chart_ratio/100.0));

   if(Chart_ratio<100)//if the balance chart is taking up the entire table
     {
      for(int i=0;i<10;i++)//create columns
        {
         for(int j=0;j<6;j++)//and rows
           {
            cells[i][j].Create(Chart.ChartId(),"InfBoard "+IntegerToString(i)+" "+IntegerToString(j),
                               wnd,j*(x_size/6.0),i*(chart_border/10.0),x_size/6.0,chart_border/10.0);
            //set selectable property to false
            cells[i][j].Selectable(false);
            //set text as read only
            cells[i][j].ReadOnly(true);
            //set font size
            cells[i][j].FontSize(GetFontSize(x_size/6.0, chart_border/10.0));
            cells[i][j].Font("Arial");    //font name
            cells[i][j].Color(text_color);//font color
           }
        }
     }

   if(Chart_ratio>0)//if the balance chart is required
     {
      //create a balance chart
      BalanceChart.Create(Chart.ChartId(), "InfBoard chart", wnd, 0, chart_border);
      //set selectable property to false
      BalanceChart.Selectable(false);
      //create a pie chart
      PieChart.Create(Chart.ChartId(), "InfBoard pie_chart", wnd, x_size*0.75, chart_border);
      PieChart.Selectable(false);//set selectable property to false
     }

   Refresh();//refresh the board
  }

Tüm öğelerin kompakt bir düzenlemesi için ilk olarak CChart sınıfının WidthInPixels() ve GetInteger() yöntemlerini kullanarak, panonun bulunacağı gösterge alt penceresinin uzunluğunu ve genişliğini öğrenin. Daha sonra CChartObjectEdit sınıfının Create() yöntemini kullanarak göstergelerin değerlerini içerecek hücreleri oluştururuz ("giriş alanını" oluşturur), tüm türevler bu CChartObject yöntemine sahiptir.

Bu tür işlemler için Standart Kitaplığı kullanmanın ne kadar uygun olduğunu unutmayın. Bu olmadan, ObjectCreate fonksiyonunu kullanarak her nesneyi oluşturmamız ve ObjectSet gibi fonksiyonları kullanarak nesnelerin özelliklerini ayarlamamız gerekir, bu da kod artıklığına yol açar. Daha sonra nesnelerin özelliklerini değiştirmek istediğimizde, karışıklığı önlemek için nesnelerin adlarını dikkatlice kontrol etmek gerekir. Şimdi sadece bir dizi grafik nesnesi oluşturabilir ve bunu istediğimiz gibi inceleyebiliriz.

Ayrıca, CChartObject sınıfının Color() yöntemi gibi sınıfın aşırı yüklenmiş oluşturucuları ise bir fonksiyon kullanarak nesnelerin özelliklerini alabilir/ayarlayabiliriz. Parametrelerle çağrıldığında onları, parametresiz olarak ayarlar - nesne rengini döndürür. Dilim grafiği denge grafiğinin yanına yerleştirin, ekranın toplam genişliğinin dörtte birini kapsayacaktır.

Refresh method() panoyu güncelleştirir. Bu güncelleştirme nelerden oluşur? Göstergeleri saymamız, grafik nesnelerine girmemiz ve bulunduğu pencerenin boyutu değiştirilmişse panoyu yeniden ölçeklendirmemiz gerekir. Pano, pencerenin tüm boş alanını kaplamalıdır.

//+------------------------------------------------------------------+
///Function of the board updating
//+------------------------------------------------------------------+
void Board::Refresh()
  {
   //check the server connection status
   if(!TerminalInfoInteger(TERMINAL_CONNECTED)) {Alert("No connection with the trading server!"); return;}
   //check the permission for importing functions from DLL
   if(!TerminalInfoInteger(TERMINAL_DLLS_ALLOWED)) {Alert("DLLs are prohibited!"); return;}
   //calculate the characteristics
   Calculate();
   //retrieve the width
   int x_size=Chart.WidthInPixels();
   //and the height of the indicator window
   int y_size=Chart.GetInteger(CHART_HEIGHT_IN_PIXELS,wnd);
   //calculate how much space the balance chart will take up
   double chart_border=y_size*(1.0-(Chart_ratio/100.0));

   string captions[10][6]= //array with signatures of interface elements
     {
        {"Total Net Profit:"," ","Gross Profit:"," ","Gross Loss:"," "},
        {"Profit Factor:"," ","Expected Payoff:"," ","",""},
        {"Absolute Drawdown:"," ","Maximal Drawdown:"," ","Relative Drawdown:"," "},
        {"Total Trades:"," ","Short Positions (won %):"," ","Long Positions (won %):"," "},
        {"","","Profit Trades (% of total):"," ","Loss trades (% of total):"," "},
        {"Largest","","profit trade:"," ","loss trade:"," "},
        {"Average","","profit trade:"," ","loss trade:"," "},
        {"Maximum","","consecutive wins ($):"," ","consecutive losses ($):"," "},
        {"Maximal","","consecutive profit (count):"," ","consecutive loss (count):"," "},
        {"Average","","consecutive wins:"," ","consecutive losses:"," "}
     };

   //put the calculated characteristics into the array
   captions[0][1]=DoubleToString(net_profit, 2);
   captions[0][3]=DoubleToString(gross_profit, 2);
   captions[0][5]=DoubleToString(gross_loss, 2);

   captions[1][1]=DoubleToString(profit_factor, 2);
   captions[1][3]=DoubleToString(expected_payoff, 2);

   captions[2][1]=DoubleToString(absolute_drawdown, 2);
   captions[2][3]=DoubleToString(maximal_drawdown, 2)+"("+DoubleToString(maximal_drawdown_pp, 2)+"%)";
   captions[2][5]=DoubleToString(relative_drawdown_pp, 2)+"%("+DoubleToString(relative_drawdown, 2)+")";

   captions[3][1]=IntegerToString(total);
   captions[3][3]=IntegerToString(short_positions)+"("+DoubleToString(short_positions_won, 2)+"%)";
   captions[3][5]=IntegerToString(long_positions)+"("+DoubleToString(long_positions_won, 2)+"%)";

   captions[4][3]=IntegerToString(profit_trades)+"("+DoubleToString(profit_trades_pp, 2)+"%)";
   captions[4][5]=IntegerToString(loss_trades)+"("+DoubleToString(loss_trades_pp, 2)+"%)";

   captions[5][3]=DoubleToString(largest_profit_trade, 2);
   captions[5][5]=DoubleToString(largest_loss_trade, 2);

   captions[6][3]=DoubleToString(average_profit_trade, 2);
   captions[6][5]=DoubleToString(average_loss_trade, 2);

   captions[7][3]=IntegerToString(maximum_consecutive_wins)+"("+DoubleToString(maximum_consecutive_wins_usd, 2)+")";
   captions[7][5]=IntegerToString(maximum_consecutive_losses)+"("+DoubleToString(maximum_consecutive_losses_usd, 2)+")";

   captions[8][3]=DoubleToString(maximum_consecutive_profit_usd, 2)+"("+IntegerToString(maximum_consecutive_profit)+")";
   captions[8][5]=DoubleToString(maximum_consecutive_loss_usd, 2)+"("+IntegerToString(maximum_consecutive_loss)+")";

   captions[9][3]=IntegerToString(average_consecutive_wins);
   captions[9][5]=IntegerToString(average_consecutive_losses);

   if(Chart_ratio<100) //if the balance chart doesn't take up the entire table
     {
      for(int i=0;i<10;i++) //go through the interface elements
        {
         for(int j=0;j<6;j++)
           {
            //specify the position
            cells[i][j].X_Distance(j*(x_size/6.0));
            cells[i][j].Y_Distance(i*(chart_border/10.0));
            //the size
            cells[i][j].X_Size(x_size/6.0);
            cells[i][j].Y_Size(chart_border/10.0);
            //the text
            cells[i][j].SetString(OBJPROP_TEXT,captions[i][j]);
            //and font size
            cells[i][j].FontSize(GetFontSize(x_size/6.0,chart_border/10.0));
           }
        }
     }

   if(Chart_ratio>0)//if the balance chart is required
     {
      //refresh the balance chart
      int X=x_size*0.75,Y=y_size-chart_border;
      //get the chart
      GetChart(X,Y,CreateGoogleRequest(X,Y,true),"board_balance_chart");
      //set its position
      BalanceChart.Y_Distance(chart_border);
      //specify file names
      BalanceChart.BmpFileOn("board_balance_chart.bmp");
      BalanceChart.BmpFileOff("board_balance_chart.bmp");
      //refresh the pie chart
      X=x_size*0.25;
      //get the chart
      GetChart(X,Y,CreateGoogleRequest(X,Y,false),"pie_chart");
      //set its new position
      PieChart.Y_Distance(chart_border);
      PieChart.X_Distance(x_size*0.75);
      //specify file names
      PieChart.BmpFileOn("pie_chart.bmp");
      PieChart.BmpFileOff("pie_chart.bmp");
     }

   ChartRedraw(); //redraw the chart
  }

CreateInterface() yöntemine benzer birçok kod vardır, önce Calculate() fonksiyonu göstergeleri hesaplar, sonra bunlar grafik nesnelerine girilir ve aynı anda nesne boyutları X_Size() ve Y_Size() yöntemleri kullanılarak pencerelerin boyutlarına ayarlanır. X_Distance ve Y_Distance yöntemleri nesnenin konumunu değiştirir.

GetFontSize() fonksiyonuna dikkat edin, metnin yeniden ölçeklendirildikten sonra kapsayıcının kenarlıklarından "taşmasına" neden olmayacak ve bunun aksine çok küçük olmayacak bir yazı tipi boyutu seçer.

Bu fonksiyonu daha yakından ele alalım:

//import DLL function for string metrics
#import "String_Metrics.dll" 
void GetStringMetrics(int font_size,int &X,int &Y);
#import

//+------------------------------------------------------------------+
///Function of determining the optimum font size
//+------------------------------------------------------------------+
int Board::GetFontSize(int x,int y)
  {
   int res=8;
   for(int i=15;i>=1;i--)//go through the different font sizes
     {
      int X,Y; //here we input the line metrics
      //determine the metrics
      GetStringMetrics(i,X,Y);
      //if the line fits the set borders - return the font size
      if(X<=x && Y<=y) return i;
     }
   return res;
  }

GetStringMetrics() fonksiyonu, kodu DLL_Sources.zip arşivinde bulunabilen ve gerekirse değiştirilebilen yukarıda açıklanan DLL'den içe aktarılır. Projede kendi arayüzünüzü tasarlamayı seçerseniz kullanışlı olabileceğini düşünüyorum.

Kullanıcı arayüzünü bitirdik, şimdi alım satım göstergelerinin hesaplanmasına dönelim.


4. Alım satım göstergelerinin hesaplanması

Calculate() yöntemi hesaplamaları gerçekleştirir.

Ancak gerekli verileri alan GetData() yöntemine de ihtiyacımız var:

//+------------------------------------------------------------------+
///Function of receiving the deals and balance data
//+------------------------------------------------------------------+
void Board::GetData()
  {
   //delete old data
   Data.Shutdown();
   ChartData.Shutdown();
   pie_data.Shutdown();
   //prepare all the deals history
   HistorySelect(0,TimeCurrent()); 
   CAccountInfo acc_inf;   //object for work with account
   //calculate the balance
   double balance=acc_inf.Balance();
   double store=0; //balance
   long_positions=0;
   short_positions=0;
   long_positions_won=0;
   short_positions_won=0;
   for(int i=0;i<HistoryDealsTotal();i++) //go through all of the deals in the history

     {
      CDealInfo deal;  //the information about the deals will be stored here
      deal.Ticket(HistoryDealGetTicket(i));//get deal ticket
      //if the trade had a financial result (exit of the market)
      if(deal.Ticket()>=0 && deal.Entry()==DEAL_ENTRY_OUT)
        {
         pie_data.Add(deal.Symbol()); //add data for the pie chart
         //check for the symbol 
         if(!For_all_symbols && deal.Symbol()!=Symbol()) continue;
         double profit=deal.Profit(); //retrieve the trade profit
         profit+=deal.Swap();         //swap
         profit+=deal.Commission();   //commission
         store+=profit;               //cumulative profit
         Data.Add(new CArrayDouble);  //add new element to the array
         ((CArrayDouble *)Data.At(Data.Total()-1)).Add(profit);  //and data
         ((CArrayDouble *)Data.At(Data.Total()-1)).Add(deal.Type());
        }
     }

   //calculate the initial deposit
   double initial_deposit=(balance-store);
   for(int i=0;i<Data.Total();i++) //go through the prepared trades
     {
      //calculate the balance value
      initial_deposit+=((CArrayDouble *)Data.At(i)).At(0);
      ChartData.Add(initial_deposit); //and put it to the array
     }
  }

İlk olarak, veri depolama yöntemini ele alalım. Standart kitaplık, dizileri kullanmaktan kaçınmanıza izin veren veri yapıları sınıflarını sağlar. Kâr ve işlem türleri hakkındaki verileri geçmişte saklayacağımız iki boyutlu bir diziye ihtiyacımız var. Ancak Standart Kitaplık iki boyutlu dizileri düzenlemek için açık sınıflar sağlamaz, ancak CArrayDouble (çift veri türü dizisi) ve CArrayObj sınıfları (CObject sınıfı örneklere ve türevlere dinamik işaretçi dizisi) vardır. Yani çift türünde dizilerin dizisi oluşturabiliriz ve tam olarak yapılan da budur. 

Tabii ki ((CArrayDouble *) Data.At (Data.Total () - 1 )) gibi bildirimler olacaktır. (Kâr) ekle veri kadar düzgün görünmüyor veri [i] [j] = kâr kadar net görünmüyor, ancak bu sadece ilk bakışta böyledir. Sonuçta, standart kitaplık sınıflarını kullanmadan yalnızca bir dizi bildirerek, yerleşik bir bellek yöneticisi, farklı bir dizi ekleme, dizileri karşılaştırma, öğe bulma vb. avantajlara sahip olamayız. Böylece, bellek organizasyonu sınıflarının kullanımı bizi dizinin taşmasını kontrol etme ihtiyacından kurtarır ve bize birçok yararlı enstrüman sağlar. 

CArray (bkz. Şekil 1.) sınıfının Total() yöntemi dizideki öğelerin sayısını verir, Add() yöntemi bunları ekler, At() yöntemi öğeleri döndürür.

Bir dilim grafik oluşturmaya karar verdiğimizden, semboller için sözleşmelerin sayısını görüntülemek amacıyla gerekli verileri toplamamız gerekir.

Bu verileri toplamak için bir yardımcı sınıf yazacağız:

//+------------------------------------------------------------------+
///The Pie chart class
//+------------------------------------------------------------------+
class PieData
  {
protected:
///number of deals per symbol
   CArrayInt         val;   
///symbols
   CArrayString      symb;  
public:
///delete the data
   bool Shutdown()          
     {
      bool res=true;
      res&=val.Shutdown();
      res&=symb.Shutdown();
      return res;
     }
///search for a sting in the array
   int Search(string str)   
     {  //check all array elements
      for(int i=0;i<symb.Total();i++)
         if(symb.At(i)==str) return i;
      return -1;
     }
///add new data
   void Add(string str)    
     {
      int symb_pos=Search(str);//determine symbol position in the array
      if(symb_pos>-1)
         val.Update(symb_pos,val.At(symb_pos)+1);//update the deals data
      else //if there isn't such a symbol yet
        {
         symb.Add(str); //add it
         val.Add(1);
        }
     }

   int Total() const {return symb.Total();}
   int Get_val(int pos) const {return val.At(pos);}
   string Get_symb(int pos) const {return symb.At(pos);}
  };

Standart kitaplık sınıflarının bize iş için gerekli yöntemleri sunması her zaman mümkün değildir. Bu örnekte, CArrayString sınıfının Search() yöntemi uygun değildir, çünkü uygulamak için önce veri yapısını ihlal eden diziyi çözmemiz gerekir. Bu nedenle kendi yöntemimizi yazmak zorunda kaldık.

Alım satım özelliklerinin hesaplanması Calculate() yönteminde uygulanır:

//+------------------------------------------------------------------+
///Calculation of characteristics
//+------------------------------------------------------------------+
void Board::Calculate()
  {
   //get the data
   GetData();
   //zero all characteristics
   gross_profit=0;
   gross_loss=0;
   net_profit=0;
   profit_factor=0;
   expected_payoff=0;
   absolute_drawdown=0;
   maximal_drawdown_pp=0;
   maximal_drawdown=0;
   relative_drawdown=0;
   relative_drawdown_pp=0;
   total=Data.Total();
   long_positions=0;
   long_positions_won=0;
   short_positions=0;
   short_positions_won=0;
   profit_trades=0;
   profit_trades_pp=0;
   loss_trades=0;
   loss_trades_pp=0;
   largest_profit_trade=0;
   largest_loss_trade=0;
   average_profit_trade=0;
   average_loss_trade=0;
   maximum_consecutive_wins=0;
   maximum_consecutive_wins_usd=0;
   maximum_consecutive_losses=0;
   maximum_consecutive_losses_usd=0;
   maximum_consecutive_profit=0;
   maximum_consecutive_profit_usd=0;
   maximum_consecutive_loss=0;
   maximum_consecutive_loss_usd=0;
   average_consecutive_wins=0;
   average_consecutive_losses=0;

   if(total==0) return; //there isn't deals - return from the function
   double max_peak=0,min_peak=0,tmp_balance=0;
   int max_peak_pos=0,min_peak_pos=0;
   int max_cons_wins=0,max_cons_losses=0;
   double max_cons_wins_usd=0,max_cons_losses_usd=0;
   int avg_win=0,avg_loss=0,avg_win_cnt=0,avg_loss_cnt=0;

   for(int i=0; i<total; i++)
     {
      double profit=((CArrayDouble *)Data.At(i)).At(0); //get profit
      int deal_type=((CArrayDouble *)Data.At(i)).At(1); //and deal type
      switch(deal_type) //check deal type
        {
         //and calculate number of long and short positions
         case DEAL_TYPE_BUY: {long_positions++; if(profit>=0) long_positions_won++; break;}
         case DEAL_TYPE_SELL: {short_positions++; if(profit>=0) short_positions_won++; break;}
        }

      if(profit>=0)//the deal is profitable
        {
         gross_profit+=profit; //gross profit
         profit_trades++;      //number of profit deals
         //the largest profitable trade and the largest profitable series
         if(profit>largest_profit_trade) largest_profit_trade=profit;

         if(maximum_consecutive_losses<max_cons_losses || 
            (maximum_consecutive_losses==max_cons_losses && maximum_consecutive_losses_usd>max_cons_losses_usd))
           {
            maximum_consecutive_losses=max_cons_losses;
            maximum_consecutive_losses_usd=max_cons_losses_usd;
           }
         if(maximum_consecutive_loss_usd>max_cons_losses_usd || 
            (maximum_consecutive_loss_usd==max_cons_losses_usd && maximum_consecutive_losses<max_cons_losses))
           {
            maximum_consecutive_loss=max_cons_losses;
            maximum_consecutive_loss_usd=max_cons_losses_usd;
           }
         //average profit per deal
         if(max_cons_losses>0) {avg_loss+=max_cons_losses; avg_loss_cnt++;}
         max_cons_losses=0;
         max_cons_losses_usd=0;
         max_cons_wins++;
         max_cons_wins_usd+=profit;
        }
      else //deal is losing
        {
         gross_loss-=profit; //cumulative profit
         loss_trades++;      //number of losing deals
         //the most unprofitable deal and the most unprofitable series
         if(profit<largest_loss_trade) largest_loss_trade=profit;
         if(maximum_consecutive_wins<max_cons_wins || 
            (maximum_consecutive_wins==max_cons_wins && maximum_consecutive_wins_usd<max_cons_wins_usd))
           {
            maximum_consecutive_wins=max_cons_wins;
            maximum_consecutive_wins_usd=max_cons_wins_usd;
           }
         if(maximum_consecutive_profit_usd<max_cons_wins_usd || 
            (maximum_consecutive_profit_usd==max_cons_wins_usd && maximum_consecutive_profit<max_cons_wins))
           {
            maximum_consecutive_profit=max_cons_wins;
            maximum_consecutive_profit_usd=max_cons_wins_usd;
           }
         //average lose per deal
         if(max_cons_wins>0) {avg_win+=max_cons_wins; avg_win_cnt++;}
         max_cons_wins=0;
         max_cons_wins_usd=0;
         max_cons_losses++;
         max_cons_losses_usd+=profit;
        }

      tmp_balance+=profit; //absolute drawdown calculation
      if(tmp_balance>max_peak) {max_peak=tmp_balance; max_peak_pos=i;}
      if(tmp_balance<min_peak) {min_peak=tmp_balance; min_peak_pos=i;}
      if((max_peak-min_peak)>maximal_drawdown && min_peak_pos>max_peak_pos) maximal_drawdown=max_peak-min_peak;
     }
   //maximal drawdown calculation
   double min_peak_rel=max_peak;
   tmp_balance=0;
   for(int i=max_peak_pos;i<total;i++)
     {
      double profit=((CArrayDouble *)Data.At(i)).At(0);
      tmp_balance+=profit;
      if(tmp_balance<min_peak_rel) min_peak_rel=tmp_balance;
     }
   //relative drawdown calculation
   relative_drawdown=max_peak-min_peak_rel;
   //net profit
   net_profit=gross_profit-gross_loss;
   //profit factor
   profit_factor=(gross_loss!=0) ?  gross_profit/gross_loss : gross_profit;
   //expected payoff
   expected_payoff=net_profit/total;
   double initial_deposit=AccountInfoDouble(ACCOUNT_BALANCE)-net_profit;
   absolute_drawdown=MathAbs(min_peak); 
   //drawdowns
   maximal_drawdown_pp=(initial_deposit!=0) ?(maximal_drawdown/initial_deposit)*100.0 : 0;
   relative_drawdown_pp=((max_peak+initial_deposit)!=0) ?(relative_drawdown/(max_peak+initial_deposit))*100.0 : 0;
   
   //profit and losing trade percentage
   profit_trades_pp=((double)profit_trades/total)*100.0;
   loss_trades_pp=((double)loss_trades/total)*100.0;
   
   //average profitable and losing deals
   average_profit_trade=(profit_trades>0) ? gross_profit/profit_trades : 0;
   average_loss_trade=(loss_trades>0) ? gross_loss/loss_trades : 0;
   
   //maximum consecutive losses
   if(maximum_consecutive_losses<max_cons_losses || 
      (maximum_consecutive_losses==max_cons_losses && maximum_consecutive_losses_usd>max_cons_losses_usd))
     {
      maximum_consecutive_losses=max_cons_losses;
      maximum_consecutive_losses_usd=max_cons_losses_usd;
     }
   if(maximum_consecutive_loss_usd>max_cons_losses_usd || 
      (maximum_consecutive_loss_usd==max_cons_losses_usd && maximum_consecutive_losses<max_cons_losses))
     {
      maximum_consecutive_loss=max_cons_losses;
      maximum_consecutive_loss_usd=max_cons_losses_usd;
     }

   if(maximum_consecutive_wins<max_cons_wins || 
      (maximum_consecutive_wins==max_cons_wins && maximum_consecutive_wins_usd<max_cons_wins_usd))
     {
      maximum_consecutive_wins=max_cons_wins;
      maximum_consecutive_wins_usd=max_cons_wins_usd;
     }
   if(maximum_consecutive_profit_usd<max_cons_wins_usd || 
      (maximum_consecutive_profit_usd==max_cons_wins_usd && maximum_consecutive_profit<max_cons_wins))
     {
      maximum_consecutive_profit=max_cons_wins;
      maximum_consecutive_profit_usd=max_cons_wins_usd;
     }
   //average loss and profit
   if(max_cons_losses>0) {avg_loss+=max_cons_losses; avg_loss_cnt++;}
   if(max_cons_wins>0) {avg_win+=max_cons_wins; avg_win_cnt++;}
   average_consecutive_wins=(avg_win_cnt>0) ? round((double)avg_win/avg_win_cnt) : 0;
   average_consecutive_losses=(avg_loss_cnt>0) ? round((double)avg_loss/avg_loss_cnt) : 0;
   
   //number of profitable long and short positions
   long_positions_won=(long_positions>0) ?((double)long_positions_won/long_positions)*100.0 : 0;
   short_positions_won=(short_positions>0) ?((double)short_positions_won/short_positions)*100.0 : 0;
  }

5. Denge grafiği oluşturmak için Google Chart API'sini kullanma

Google Chart API’si geliştiricilerin çeşitli türlerde diyagramları anında oluşturmasına olanak tanır. Google Chart API'si, Google'ın web sunucularındaki kaynağa olan (URL) bağlantıda depolanır ve doğru biçimlendirilmiş bir bağlantı (URL) alındığında diyagramı görüntü olarak verir.

Diyagram özellikleri (renkler, üstbilgiler, eksen, grafikteki noktalar vb.) bağlantı (URL) tarafından belirtilir. Elde edilen görüntü bir dosya sisteminde veya veritabanında depolanabilir. En memnun edici yanı, Google Chart API'sinin ücretsiz olması ve bir hesaba sahip olmayı veya kayıt sürecinden geçmeyi gerektirmemesidir. 

GetChart() yöntemi, grafiği Google'dan alır ve diske kaydeder:

#import "PNG_to_BMP.dll"//import of DLL with the function of conversion of PNG images to BMP
bool Convert_PNG(string src,string dst);
#import

#import "wininet.dll"//import DLL with the function for working with the internet
int InternetAttemptConnect(int x);
int InternetOpenW(string sAgent,int lAccessType,
                  string sProxyName="",string sProxyBypass="",
                  int lFlags=0);
int InternetOpenUrlW(int hInternetSession,string sUrl,
                     string sHeaders="",int lHeadersLength=0,
                     int lFlags=0,int lContext=0);
int InternetReadFile(int hFile,char &sBuffer[],int lNumBytesToRead,
                     int &lNumberOfBytesRead[]);
int InternetCloseHandle(int hInet);
#import

//+------------------------------------------------------------------+
///Function of creating a balance chart
//+------------------------------------------------------------------+
void Board::GetChart(int X_size,int Y_size,string request,string file_name)
  {
   if(X_size<1 || Y_size<1) return; //too small
   //try to create connection
   int rv=InternetAttemptConnect(0);
   if(rv!=0) {Alert("Error in call of the InternetAttemptConnect()"); return;}
   //initialize the structures
   int hInternetSession=InternetOpenW("Microsoft Internet Explorer", 0, "", "", 0);
   if(hInternetSession<=0) {Alert("Error in call of the InternetOpenW()"); return;}
   //send request
   int hURL=InternetOpenUrlW(hInternetSession, request, "", 0, 0, 0);
   if(hURL<=0) Alert("Error in call of the InternetOpenUrlW()");
   //file with the result
   CFileBin chart_file;
   //let's create it
   chart_file.Open(file_name+".png",FILE_BIN|FILE_WRITE);
   int dwBytesRead[1]; //number of data read
   char readed[1000];  //the data 
   //read the data, returned by server after the request
   while(InternetReadFile(hURL,readed,1000,dwBytesRead))
     {
      if(dwBytesRead[0]<=0) break; //no data - exit
      chart_file.WriteCharArray(readed,0,dwBytesRead[0]); //write data to file
     }
   InternetCloseHandle(hInternetSession);//close connection
   chart_file.Close();//close file
   //******************************
   //prepare the paths for the converter
   CString src;
   src.Assign(TerminalInfoString(TERMINAL_PATH));
   src.Append("\MQL5\Files\\"+file_name+".png");
   src.Replace("\\","\\\\");
   CString dst;
   dst.Assign(TerminalInfoString(TERMINAL_PATH));
   dst.Append("\MQL5\Images\\"+file_name+".bmp");
   dst.Replace("\\","\\\\");
   //convert the file
   if(!Convert_PNG(src.Str(),dst.Str())) Alert("Error in call of the Convert_PNG()");
  }
  

API Windows ve MQL5'in çevrimiçi araçlarıyla çalışmanın ayrıntılarını internet üzerinden terminaller arasında veri alışverişi için WinInet.dll kullanma makalesinde bulabilirsiniz. Bu yüzden buna zaman ayırmayacağım. İçe aktarılan Convert_PNG() fonksiyonu, PNG görüntülerini BMP'ye dönüştürmek için benim tarafımdan yazıldı.

Google Chart, grafikleri PNG veya GIF biçiminde verdiği ve "grafik etiketi" nesnesi yalnızca BMP görüntülerini kabul ettiği için gereklidir. İlgili PNG_to_BMP.dll kitaplık fonksiyonlarının kodu DLL_Sources.zip arşivinde bulunabilir.

Bu fonksiyon, standart kitaplığı kullanarak satırlar ve dosyalarla çalışmanın bazı örneklerini de gösterir. CString sınıfı yöntemler, Dizgi Fonksiyonları ile aynı işlemlerin yürütülmesine izin verir. CFile sınıfı; CFileBin ve CFileTxt sınıfları için bir temel sınıftır. Onların yardımıyla, sırasıyla ikili ve metin dosyalarının okunmasını ve kaydedilmesini sağlayabiliriz. Yöntemler, dosyalarla çalışma fonksiyonlarına benzer.

Son olarak, dengedeki verilerden sorgular oluşturan CreateGoogleRequest () fonksiyonunu açıklayacağız:

//+------------------------------------------------------------------+
///Function for creating a request for the Google Charts server
//+------------------------------------------------------------------+
string Board::CreateGoogleRequest(int X_size,int Y_size,bool type)
  {
   if(X_size>1000) X_size=1000; //check the chart size
   if(Y_size>1000) Y_size=300;  //to make sure it is not too large
   if(X_size<1) X_size=1;       //and small//s18>
   if(Y_size<1) Y_size=1;
   if(X_size*Y_size>300000) {X_size=1000; Y_size=300;}//and fit the area
   CString res; //string with results
   if(type) //create request for the balance chart
     {
      //prepare the request
      res.Assign("http://chart.apis.google.com/chart?cht=lc&chs=");
      res.Append(IntegerToString(X_size));
      res.Append("x");
      res.Append(IntegerToString(Y_size));
      res.Append("&chd=t:");
      for(int i=0;i<ChartData.Total();i++)
         res.Append(DoubleToString(ChartData.At(i),2)+",");
      res.TrimRight(",");
      //sort array
      ChartData.Sort();
      res.Append("&chxt=x,r&chxr=0,0,");
      res.Append(IntegerToString(ChartData.Total()));
      res.Append("|1,");
      res.Append(DoubleToString(ChartData.At(0),2)+",");
      res.Append(DoubleToString(ChartData.At(ChartData.Total()-1),2));
      res.Append("&chg=10,10&chds=");
      res.Append(DoubleToString(ChartData.At(0),2)+",");
      res.Append(DoubleToString(ChartData.At(ChartData.Total()-1),2));
     }
   else //create request for the pie chart
     {
      //prepare the request
      res.Assign("http://chart.apis.google.com/chart?cht=p3&chs=");
      res.Append(IntegerToString(X_size));
      res.Append("x");
      res.Append(IntegerToString(Y_size));
      res.Append("&chd=t:");
      for(int i=0;i<pie_data.Total();i++)
         res.Append(IntegerToString(pie_data.Get_val(i))+",");
      res.TrimRight(",");
      res.Append("&chdl=");
      for(int i=0;i<pie_data.Total();i++)
         res.Append(pie_data.Get_symb(i)+"|");
      res.TrimRight("|");
      res.Append("&chco=");
      int cnt=0;
      for(int i=0;i<pie_data.Total();i++)
        {
         if(cnt>11) cnt=0;
         res.Append(colors[cnt]+"|");
         cnt++;
        }
      res.TrimRight("|");
     }
   return res.Str(); //return the result
  }

Denge grafiği ve dilim grafik isteklerinin ayrı olarak toplandığını unutmayın. Append() yöntemi mevcut satırın sonuna başka bir satır ekler ve TrimRight() yöntemi satırın sonunda görüntülenen fazladan karakterleri kaldırmanıza olanak tanır.


6. Son birleştirme ve test

Sınıf hazır, test edelim. OnInit () göstergesi ile başlıyoruz:

Board *tablo;   //pointer to the board object
int prev_x_size=0,prev_y_size=0,prev_deals=0;
//+------------------------------------------------------------------+
//| Custom indicator initialization function                         |
//+------------------------------------------------------------------+
int OnInit()
  {
//--- indicator buffers mapping
   //set indicator short name
   IndicatorSetString(INDICATOR_SHORTNAME,"IT");
   //launch the timer
   EventSetTimer(1); 
   //create object instance
   tablo=new Board;
   //and the interface
   tablo.CreateInterface(); 
   prev_deals=HistoryDealsTotal(); //number of deals
   //current sizes of the window
   prev_x_size=ChartGetInteger(0,CHART_WIDTH_IN_PIXELS); 
   prev_y_size=ChartGetInteger(0, CHART_HEIGHT_IN_PIXELS);
//---
   return(0);
  }

Burada dinamik olarak Pano sınıfı örneğini oluşturuyoruz, zamanlayıcıyı başlatıyoruz, yardımcı değişkenleri başlatıyoruz. 

Hemen OnDeinit() fonksiyonunu yerleştiriyoruz, orada nesneyi kaldıracağız (yıkıcıyı otomatik olarak çağırır) ve zamanlayıcıyı durduracağız:

void OnDeinit(const int reason)
{
   EventKillTimer(); //stop the timer
   delete table;    //and board
}

OnCalculate fonksiyonu aşağıdaki durumda yeni sözleşmelerin akışını her bir tik ile izler, ekranı günceller:

int OnCalculate(const int rates_total,
                const int prev_calculated,
                const datetime &time[],
                const double &open[],
                const double &high[],
                const double &low[],
                const double &close[],
                const long &tick_volume[],
                const long &volume[],
                const int &spread[])
  {
//---
   //prepare the history
   HistorySelect(0,TimeCurrent());
   int deals=HistoryDealsTotal();
   //update the board if number of deals has changed
   if(deals!=prev_deals) tablo.Refresh();
   prev_deals=deals;
//--- return value of prev_calculated for next call
   return(rates_total);
  }

OnTimer() fonksiyonu pencerenin boyutundaki değişiklikleri izler ve gerekirse ekran boyutunu özelleştirir, ayrıca onayların saniyede 1'den daha nadir gelmesi durumunda tıpkı OnCalculate() gibi sözleşmeleri izler.

void OnTimer()
  {
   int x_size=ChartGetInteger(0, CHART_WIDTH_IN_PIXELS);
   int y_size=ChartGetInteger(0, CHART_HEIGHT_IN_PIXELS);
   //update the board if window size has changed
   if(x_size!=prev_x_size || y_size!=prev_y_size) tablo.Refresh();
   prev_x_size=x_size;
   prev_y_size=y_size;
   //update the board if number of deals has changed
   HistorySelect(0,TimeCurrent());
   int deals=HistoryDealsTotal();
   if(deals!=prev_deals) tablo.Refresh();
   prev_deals=deals;
  }

Göstergeyi derleyin ve çalıştırın:

Şekil 3.Tablonun son görünümü

Şekil 3. Tablonun son görünümü

Sonuç

Sevgili okuyucu, umarım bu makaleyi okurken kendiniz için yeni bir şey bulursunuz. Standart Kitaplık gibi harika bir enstrümanın tüm potansiyellerini sizden önce ortaya çıkarmaya çalıştım çünkü kolaylık, hız ve yüksek performans kalitesi sağlar. Tabii ki OOP hakkında biraz bilgi sahibi olmanız gerekir.

İyi şanslar. 

Başlamak için MQL5.rar arşivini terminal klasörüne çıkartın ve DLL kullanımına izin verin. DLL_Sources.zip arşivi, String_Metrics.dll PNG_to_BMP.dll kitaplıklarının kaynak kodlarını içerir, bunlar Borland C++ Builder ortamında yüklü bir GDI ile benim tarafımdan yazılmıştır. 

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

Ekli dosyalar |
infoboard.zip (200 KB)
dll_sources.zip (1.62 KB)
infoboard-doc-en.zip (1836.47 KB)
Uzman Danışmanda Para Yönetimi için fonksiyonlar Uzman Danışmanda Para Yönetimi için fonksiyonlar
Alım satım stratejilerinin geliştirilmesi, öncelikli olarak, piyasaya giriş ve çıkış için kalıpların aranmasının yanı sıra pozisyonların muhafaza edilmesine odaklanmaktadır. Eğer bazı modelleri otomatik alım salım için kurallar halinde resmileştirebiliyorsak, bu durumda yatırımcı, pozisyonların hacmini, marjların büyüklüğünü hesaplamanın yanı sıra otomatikleştirilmiş bir açık pozisyonu güvence altına almak için güvenli bir ipotek fonu seviyesini muhafaza etme sorunuyla karşı karşıyadır. Bu yazıda MQL5 dilini, bu hesaplamaları yürütmek için basit örnekler oluşturmak için kullanacağız.
Yeni Başlayanlar için MQL5’te Uzman Danışman Yazmak İçin Adım Adım Açıklamalı Kılavuz Yeni Başlayanlar için MQL5’te Uzman Danışman Yazmak İçin Adım Adım Açıklamalı Kılavuz
MQL5’te Uzman Danışmanların programlanması basittir ve kolayca öğrenilebilir. Bu adım adım açıklamalı kılavuzda geliştirilmiş bir alım satım stratejisine dayanarak basit bir Uzman Danışman yazmak için gereken temel adımları göreceksiniz. Uzman Danışmanın yapısı, yerleşik teknik talimatlar ve alım satım fonksiyonları, Hata Ayıklama modunun ayrıntıları ve Strateji Test Cihazının kullanımı açıklanmaktadır.
Başka Bir Göstergeye Dayalı Bir Gösterge Nasıl Yazılır? Başka Bir Göstergeye Dayalı Bir Gösterge Nasıl Yazılır?
MQL5'te, hem sıfırdan hem de zaten mevcut olan, istemci terminalinde yerleşik veya özel bir gösterge temelinde bir gösterge yazabilirsiniz. Burada ayrıca iki yolunuz var - bir göstergeyi ona yeni hesaplamalar ve grafik stilleri ekleyerek geliştirmek veya istemci terminalinde yerleşik bir gösterge veya iCustom() ya da IndicatorCreate() fonksiyonları aracılığıyla özel bir gösterge kullanmak.
MetaTrader 5 ile Yeni Fırsatlar MetaTrader 5 ile Yeni Fırsatlar
MetaTrader 4, dünyanın her yerinden yatırımcılar arasında popülerliğini kazandı ve daha fazlasının istenemeyeceği düşünülüyordu. Yüksek işlem hızı, kararlılığı, gösterge yazmak için çok çeşitli olanaklar, Uzman Danışmanlar (EA) ve bilgi işlem sistemleri ve yüzün üzerinde farklı aracı arasından seçim yapma becerisi ile terminal kendini diğerlerinden büyük ölçüde ayırdı. Ancak zaman sabit durmuyor ve kendimizi MetaTrade 4 veya MetaTrade 5 seçimiyle karşı karşıya buluyoruz. Bu makalemizde, 5. nesil terminalin mevcut lehimize olan temel farklarını anlatacağız.