Standart Kitaplık Sınıflarını ve Google Grafik API (Uygulama Programlama Arayüzü) kullanarak Bilgi Panosu oluşturmak
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:
- CObject temel sınıfı
- Veri sınıfları
- Grafik Nesneleri için sınıflar
- Çizelge üzerinde çalışmak için bir sınıf
- Dosya işlemleri için sınıflar
- Dizgi işlemleri için bir sınıf
- Göstergelerle ve zaman serileriyle çalışmak için sınıflar
- Alım-Satım Sınıfları
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ü
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:
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
- Ücretsiz ticaret uygulamaları
- İşlem kopyalama için 8.000'den fazla sinyal
- Finansal piyasaları keşfetmek için ekonomik haberler
Gizlilik ve Veri Koruma Politikasını ve MQL5.com Kullanım Şartlarını kabul edersiniz