Expert Advisor'ın Çalışması Sırasında Denge Eğrisinin Eğimini Kontrol Etme
Giriş
Bu makalede, bir geri bildirim oluşturmak yoluyla Expert Advisor'ların performansını artırmaya olanak tanıyan yaklaşımlardan biri açıklanmaktadır. Bu durumda, geri bildirim, denge eğrisinin eğiminin ölçülmesine dayalı olacaktır. İş hacmi düzenlenerek eğim kontrolü otomatik olarak yapılır. Bir Expert Advisor aşağıdaki modlarda alım satım işlemi yapabilir: Kesim hacmi ile, lotların iş miktarı ile (başlangıçta ayarlanmış olana göre) ve ara hacim ile. Çalışma modu otomatik olarak değiştirilir.
Geri bildirim zincirinde farklı düzenleyici özellikler kullanılır: Kademeli, histerezisli kademeli, doğrusal. Bu, denge eğrisinin eğimini kontrol eden sistemin belirli bir sistemin özelliklerine göre ayarlanmasını sağlar.
Ana fikir, kendi alım satım sistemini izlerken bir yatırımcı için karar verme sürecini otomatikleştirmektir. Çalışmasının elverişsiz dönemlerinde riskleri azaltmak mantıklıdır. Normal çalışma moduna dönerken riskler başlangıç düzeyine geri yüklenebilir.
Tabi ki, bu sistem her derde deva değildir ve kaybeden bir Expert Advisor'ı karlı bir sisteme dönüştürmeyecektir. Bir şekilde, bu, Expert Advisor'ın bir hesapta önemli kayıplar yaşamasını engelleyen MM'ye (para yönetimi) bir ektir.
Makale, bu işlevin herhangi bir Expert Advisor'ın koduna gömülmesine izin veren bir kitaplık içermektedir.
Çalışma Prensibi
Denge eğrisinin eğimini kontrol eden sistemin çalışma prensibine bir göz atalım. Bir alım satım Expert Advisor'ımız olduğunu varsayalım. Varsayımsal denge eğrisi şu şekilde görünür:
Şekil 1. Denge eğrisinin eğimini kontrol eden sistemin çalışma prensibi
Sabit hacimli alım satım işlemleri kullanan Expert Advisor için başlangıç dengesi eğrisi yukarıda gösterilmiştir. Kapanan alım satım işlemleri kırmızı noktalarla gösterilmektedir. Bu noktaları, alım satım işlemi sırasında Expert Advisor'ın denge değişimini temsil eden bir eğri çizgisiyle birleştirelim (kalın siyah çizgi).
Şimdi bu çizginin zaman eksenine olan eğim açısını (ince mavi çizgilerle gösterilen) sürekli olarak takip edeceğiz. Veya daha açık olmak gerekirse, her alım satım işlemini bir sinyalle açmadan önce, eğim açısını önceden kapatılmış iki alım satım işlemiyle (veya açıklamanın daha basit olması için iki alım satım işlemiyle) hesaplayacağız. Eğim açısı belirtilen değerin altına düşerse kontrol sistemimiz çalışmaya başlar, açının hesaplanan değerine ve belirtilen düzenleme işlevine göre hacmi azaltır.
Bu şekilde, alım satım işlemi başarısız bir döneme girerse hacim Т3... Т5 alım satım dönemi içinde Vmax. durumundan Vmin. durumuna düşer. Т5 noktasından sonra alım satım işlemi, belirtilen minimum hacimle alım satım işlemi hacminin reddedilme modunda gerçekleştirilir. Expert Advisor'ın karlılığı geri kazanıldığında ve denge eğrisinin eğim açısı belirlenen değerin üzerine çıktığında hacim artmaya başlar. Bu, Т8...Т10 aralığında gerçekleşir. Т10 noktasından sonra, alım satım işlem hacmi Vmax. başlangıç durumuna geri döner.
Bu düzenleme sonucunda oluşan denge eğrisi şekil 1'in alt kısmında gösterilmiştir. B1 durumundan B2 durumuna olan ilk düşüşün azaldığını ve B1 durumundan B2* durumuna düştüğünü görebilirsiniz. Ayrıca, maksimum hacmin Т8...Т10 geri kazanıldığı süre içinde karın biraz düştüğünü de gözlemleyebilirsiniz - bu, madalyonun öteki yüzüdür.
Yeşil renk, minimum belirtilen hacimle alım satım işlemi yapıldığında denge eğrisinin bir kısmını vurgular. Sarı renk, maksimumdan minimum hacme ve geriye doğru geçiş kısımlarını temsil eder. Burada birkaç geçiş varyantı mümkündür:
- Kademeli - Hacim maksimumdan minimum hacme ve geriye doğru ayrı adımlarla değişir,
- Doğrusal - Hacim, düzenlenmiş aralık içinde denge eğrisinin eğim açısına bağlı olarak doğrusal olarak değiştirilir,
- Histerezisli kademeli - Maksimumdan minimum hacme ve geriye doğru geçiş, eğim açısının fark değerlerinde gerçekleştirilir.
Bunu resimlerle gösterelim:
Şekil 2. Düzenleyici özellik türleri
Düzenleyici özellikler, kontrol sisteminin hızını etkiler - Etkinleştirme/devre dışı bırakma gecikmesi, maksimumdan minimum hacme geçiş ve geriye doğru geçiş süreci. En iyi test sonuçlarına ulaşılırken deneysel olarak bir özellik seçilmesi önerilir.
Böylece, denge eğrisinin eğim açısına dayalı geri bildirim ile alım satım sistemini geliştiriyoruz. Bu tür bir hacim düzenlemesinin, yalnızca alım satım sisteminin bir parçası olarak hacme sahip olmayan sistemler için uygun olduğunu unutmayın. Örneğin, Martingale prensibi kullanılıyorsa ilk Expert Advisor'da değişiklik yapmadan bu sistemi doğrudan kullanamazsınız.
Ayrıca, şu önemli noktalara da dikkatimizi çekmemiz gerekiyor:
- Denge çizgisinin eğimini yönetmenin etkinliği doğrudan normal çalışma modundaki iş hacminin hacim reddetme modundaki hacme oranına bağlıdır. Bu oran ne kadar büyük olursa, yönetim o kadar etkili olur. Bu nedenle, başlangıçtaki iş hacmi, mümkün olan minimum hacimden çok daha büyük olmalıdır.
- Expert Advisor dengesinin ortalama yükselme ve düşme değişim dönemi, kontrol sisteminin tepki verme süresinden oldukça büyük olmalıdır. Aksi takdirde, sistem denge eğrisinin eğimini düzenlemeyi başaramayacaktır. Ortalama sürenin tepki süresine oranı ne kadar fazlaysa sistem o kadar etkilidir. Bu gereklilik hemen hemen her otomatik düzenleme sistemini ilgilendirmektedir.
Nesne Yönelimli Programlama Kullanarak MQL5'te Uygulama
Yukarıda anlatılan yaklaşımı gerçekleştiren bir kitaplık yazalım. Bunu yapmak için, MQL5'in yeni özelliği olan nesne yönelimli yaklaşımı kullanalım. Bu yaklaşım, kodun büyük bölümlerini sıfırdan yeniden yazmadan gelecekte kitaplığımızı kolayca geliştirmemize ve genişletmemize olanak tanır.
TradeSymbol Sınıfı
Çoklu para birimi testi yeni MetaTrader 5 platformunda uygulandığı için tüm çalışmayı kendi içinde herhangi bir iş sembolü ile kapsayan bir sınıfa ihtiyacımız var. Bu, söz konusu kitaplığın çok para birimli Expert Advisor'larda kullanılmasına izin verir. Bu sınıf, kontrol sistemini doğrudan ilgilendirmez; yardımcıdır. Bu nedenle, bu sınıf, iş sembolü ile işlemler için kullanılacaktır.
//--------------------------------------------------------------------- // Operations with work symbol: //--------------------------------------------------------------------- class TradeSymbol { private: string trade_symbol; // work symbol private: double min_trade_volume; // minimum allowed volume for trade operations double max_trade_volume; // maximum allowed volume for trade operations double min_trade_volume_step; // minimum change of volume double max_total_volume; // maximum change of volume double symbol_point; // size of one point double symbol_tick_size; // minimum change of price int symbol_digits; // number of digits after decimal point protected: public: void RefreshSymbolInfo( ); // refresh market information about the work symbol void SetTradeSymbol( string _symbol ); // set/change work symbol string GetTradeSymbol( ); // get work symbol double GetMaxTotalLots( ); // get maximum cumulative volume double GetPoints( double _delta ); // get change of price in points public: double NormalizeLots( double _requied_lot ); // get normalized trade volume double NormalizePrice( double _org_price ); // get normalized price with consideration of step of change of quote public: void TradeSymbol( ); // constructor void ~TradeSymbol( ); // destructor };
Sınıfın yapısı çok basittir. Amaç, mevcut piyasa bilgilerinin belirli bir sembol ile alınması, saklanması ve işlenmesidir. Ana yöntemler şu şekildedir: TradeSymbol::RefreshSymbolInfo, TradeSymbol::NormalizeLots, TradeSymbol::NormalizePrice. Onları tek tek ele alalım.
TradeSymbol::RefreshSymbolInfo yöntemi, iş sembolü ile piyasa bilgilerini yenilemek için tasarlanmıştır.
//--------------------------------------------------------------------- // Refresh market information by work symbol: //--------------------------------------------------------------------- void TradeSymbol::RefreshSymbolInfo( ) { // If a work symbol is not set, don't do anything: if( GetTradeSymbol( ) == NULL ) { return; } // Calculate parameters necessary for normalization of volume: min_trade_volume = SymbolInfoDouble( GetTradeSymbol( ), SYMBOL_VOLUME_MIN ); max_trade_volume = SymbolInfoDouble( GetTradeSymbol( ), SYMBOL_VOLUME_MAX ); min_trade_volume_step = SymbolInfoDouble( GetTradeSymbol( ), SYMBOL_VOLUME_STEP ); max_total_volume = SymbolInfoDouble( GetTradeSymbol( ), SYMBOL_VOLUME_LIMIT ); symbol_point = SymbolInfoDouble( GetTradeSymbol( ), SYMBOL_POINT ); symbol_tick_size = SymbolInfoDouble( GetTradeSymbol( ), SYMBOL_TRADE_TICK_SIZE ); symbol_digits = ( int )SymbolInfoInteger( GetTradeSymbol( ), SYMBOL_DIGITS ); }
Birkaç yöntemde kullanılan önemli bir noktaya dikkat edin. MQL5'in mevcut gerçekleştirilmesi, parametrelerle bir oluşturucu kullanılmasına izin vermediği için iş sembollerinin birincil ayarı için şu yöntemi çağırmalısınız:
void SetTradeSymbol( string _symbol ); // set/change work symbol
TradeSymbol::NormalizeLots yöntemi, doğru ve normalize edilmiş bir hacim elde etmek için kullanılır. Bir pozisyonun boyutunun, aracı tarafından izin verilen minimum olası değerden daha küçük olamayacağını biliyoruz. Bir pozisyonun minimum değişim adımı da aracı tarafından belirlenir ve farklılık gösterebilir. Bu yöntem, alttan en yakın hacim değerini döndürür.
Ayrıca, varsayılan pozisyonun hacminin, aracı tarafından izin verilen maksimum değeri aşıp aşmadığını da kontrol eder.
//--------------------------------------------------------------------- // Get normalized trade volume: //--------------------------------------------------------------------- // - input necessary volume; // - output is normalized volume; //--------------------------------------------------------------------- double TradeSymbol::NormalizeLots( double _requied_lots ) { double lots, koeff; int nmbr; // If a work symbol is not set, don't do anything: if( GetTradeSymbol( ) == NULL ) { return( 0.0 ); } if( this.min_trade_volume_step > 0.0 ) { koeff = 1.0 / min_trade_volume_step; nmbr = ( int )MathLog10( koeff ); } else { koeff = 1.0 / min_trade_volume; nmbr = 2; } lots = MathFloor( _requied_lots * koeff ) / koeff; // Lower limit of volume: if( lots < min_trade_volume ) { lots = min_trade_volume; } // Upper limit of volume: if( lots > max_trade_volume ) { lots = max_trade_volume; } lots = NormalizeDouble( lots, nmbr ); return( lots ); }
TradeSymbol::NormalizePrice yöntemi, doğru ve normalize edilmiş fiyatı elde etmek için kullanılır. Belirli bir sembol için ondalık noktadan sonraki anlamlı basamak sayısı (fiyatın doğruluğu) belirlenmek zorunda olduğu için fiyatı kısaltmamız gerekir. Buna ek olarak, bazı semboller (örneğin, vadeli işlemler) bir noktadan daha büyük bir minimum fiyat değişikliği adımına sahiptir. Bu nedenle fiyat değerlerini minimum tutarsızlığın katı yapmalıyız.
//--------------------------------------------------------------------- // Normalization of price with consideration of step of price change: //--------------------------------------------------------------------- double TradeSymbol::NormalizePrice( double _org_price ) { // Minimal step of quote change in points: double min_price_step = NormalizeDouble( symbol_tick_size / symbol_point, 0 ); double norm_price = NormalizeDouble( NormalizeDouble(( NormalizeDouble( _org_price / symbol_point, 0 )) / min_price_step, 0 ) * min_price_step * symbol_point, symbol_digits ); return( norm_price ); }
Gerekli normalize edilmemiş fiyat, işleve girilir. Ve gerekli olana en yakın olan normalize edilmiş fiyatı döndürür.
Diğer yöntemlerin amacı yorumlarda açıkça anlatılmıştır; daha fazla açıklama gerektirmez.
TBalanceHistory Sınıfı
Bu sınıf, adından da anlaşılacağı gibi bir hesabın bakiye geçmişi ile çalışmak için tasarlanmıştır. Ayrıca aşağıda açıklanan birkaç sınıf için bir temel sınıftır. Bu sınıfın temel amacı, bir Expert Advisor'ın alım satım geçmişine erişim sağlamaktır. Ayrıca, geçmişi iş sembolüne, "sihirli sayıya", Expert Advisor'ın izlenmeye başlandığı tarihe veya aynı anda üç öğeye göre filtreleyebilirsiniz.
//--------------------------------------------------------------------- // Operations with balance history: //--------------------------------------------------------------------- class TBalanceHistory { private: long current_magic; // value of "magic number" when accessing the history of deals ( 0 - any number ) long current_type; // type of deals ( -1 - all ) int current_limit_history; // limit of depth of history ( 0 - all history ) datetime monitoring_begin_date; // date of start of monitoring history of deals int real_trades; // number of actual trades already performed protected: TradeSymbol trade_symbol; // operations with work symbol protected: // "Raw" arrays: double org_datetime_array[ ]; // date/time of trade double org_result_array[ ]; // result of trade // Arrays with data grouped by time: double group_datetime_array[ ]; // date/time of trade double group_result_array[ ]; // result of trade double last_result_array[ ]; // array for storing results of last trades ( points on the Y axis ) double last_datetime_array[ ]; // array for storing time of last trades ( points on the X axis ) private: void SortMasterSlaveArray( double& _m[ ], double& _s[ ] ); // synchronous ascending sorting of two arrays public: void SetTradeSymbol( string _symbol ); // set/change work symbol string GetTradeSymbol( ); // get work symbol void RefreshSymbolInfo( ); // refresh market information by work symbol void SetMonitoringBeginDate( datetime _dt ); // set date of start of monitoring datetime GetMonitoringBeginDate( ); // get date of start of monitoring void SetFiltrParams( long _magic, long _type = -1, int _limit = 0 );// set parameters of filtration of deals public: // Get results of last trades: int GetTradeResultsArray( int _max_trades ); public: void TBalanceHistory( ); // constructor void ~TBalanceHistory( ); // destructor };
Son alım satım işlemlerinin ve geçmişin sonuçlarını okurken filtreleme ayarları, TBalanceHistory::SetFiltrParams yöntemi kullanılarak belirlenir. Bu, aşağıdaki giriş parametrelerine sahiptir:
- _magic - Geçmişten okunması gereken alım satım işlemlerinin "sihirli sayısı". Sıfır değeri belirtilirse herhangi bir "sihirli sayı" içeren alım satım işlemleri okunacaktır.
- _type - Okunması gereken yatırımların türü. Şu değerleri içerebilir: DEAL_TYPE_BUY (yalnızca uzun alım satım işlemlerini okumak için), DEAL_TYPE_SELL (yalnızca kısa alım satım işlemlerini okumak için) ve - 1 (hem uzun hem de kısa alım satım işlemlerini okumak için).
- _limit - Analiz edilen alım satım işlemlerinin derinliğini sınırlar. Sıfıra eşitse mevcut tüm geçmiş analiz edilir.
Varsayılan olarak, TBalanceHistory sınıfının nesnesi oluşturulduğunda şu değerler ayarlanır: _magic = 0, _type = -1, _limit = 0.
Bu sınıfın ana yöntemi şu şekildedir: TBalanceHistory::GetTradeResultsArray. last_result_array ve last_datetime_array sınıf üye dizilerini son alım satım işlemlerinin sonuçlarıyla doldurmak için tasarlanmıştır. Yöntem, şu giriş parametrelerine sahiptir:
- _max_trades - Geçmişten okunması ve çıktı dizilerine yazılması gereken maksimum alım satım işlemi sayısı. Eğim açısını hesaplamak için en az iki noktaya ihtiyacımız olduğu için bu değer ikiden küçük olmamalıdır. Bu değer sıfıra eşitse mevcut tüm alım satım işlemi geçmişi analiz edilir. Pratik olarak, denge eğrisinin eğiminin hesaplanması için gerekli nokta sayısı burada belirtilir.
//--------------------------------------------------------------------- // Reads the results of last (by time) trades to arrays: //--------------------------------------------------------------------- // - returns the number of actually read trades but not more than specified; //--------------------------------------------------------------------- int TBalanceHistory::GetTradeResultsArray( int _max_trades ) { int index, limit, count; long deal_type, deal_magic, deal_entry; datetime deal_close_time, current_time; ulong deal_ticket; // ticket of deal double trade_result; string symbol, deal_symbol; real_trades = 0; // Number of trades should be no less than two: if( _max_trades < 2 ) { return( 0 ); } // If a work symbol is not specified, don't do anything: symbol = trade_symbol.GetTradeSymbol( ); if( symbol == NULL ) { return( 0 ); } // Request the history of deals and orders from the specified time to the current moment: if( HistorySelect( monitoring_begin_date, TimeCurrent( )) != true ) { return( 0 ); } // Calculate number of trades: count = HistoryDealsTotal( ); // If there are less trades in the history than it is necessary, then exit: if( count < _max_trades ) { return( 0 ); } // If there are more trades in the history than it is necessary, then limit them: if( current_limit_history > 0 && count > current_limit_history ) { limit = count - current_limit_history; } else { limit = 0; } // If needed, adjust dimension of "raw" arrays by the specified number of trades: if(( ArraySize( org_datetime_array )) != ( count - limit )) { ArrayResize( org_datetime_array, count - limit ); ArrayResize( org_result_array, count - limit ); } // Fill the "raw" array with trades from history base: real_trades = 0; for( index = count - 1; index >= limit; index-- ) { deal_ticket = HistoryDealGetTicket( index ); // If those are not closed deals, don't go further: deal_entry = HistoryDealGetInteger( deal_ticket, DEAL_ENTRY ); if( deal_entry != DEAL_ENTRY_OUT ) { continue; } // Check "magic number" of deal if necessary: deal_magic = HistoryDealGetInteger( deal_ticket, DEAL_MAGIC ); if( current_magic != 0 && deal_magic != current_magic ) { continue; } // Check symbol of deal: deal_symbol = HistoryDealGetString( deal_ticket, DEAL_SYMBOL ); if( symbol != deal_symbol ) { continue; } // Check type of deal if necessary: deal_type = HistoryDealGetInteger( deal_ticket, DEAL_TYPE ); if( current_type != -1 && deal_type != current_type ) { continue; } else if( current_type == -1 && ( deal_type != DEAL_TYPE_BUY && deal_type != DEAL_TYPE_SELL )) { continue; } // Check time of closing of deal: deal_close_time = ( datetime )HistoryDealGetInteger( deal_ticket, DEAL_TIME ); if( deal_close_time < monitoring_begin_date ) { continue; } // So, we can read another trade: org_datetime_array[ real_trades ] = deal_close_time / 60; org_result_array[ real_trades ] = HistoryDealGetDouble( deal_ticket, DEAL_PROFIT ) / HistoryDealGetDouble( deal_ticket, DEAL_VOLUME ); real_trades++; } // if there are less trades than necessary, return: if( real_trades < _max_trades ) { return( 0 ); } count = real_trades; // Sort the "raw" array by date/time of closing the order: SortMasterSlaveArray( org_datetime_array, org_result_array ); // If necessary, adjust dimension of group arrays for the specified number of points: if(( ArraySize( group_datetime_array )) != count ) { ArrayResize( group_datetime_array, count ); ArrayResize( group_result_array, count ); } ArrayInitialize( group_datetime_array, 0.0 ); ArrayInitialize( group_result_array, 0.0 ); // Fill the output array with grouped data ( group by the identity of date/time of position closing ): for( index = 0; index < count; index++ ) { // Get another trade: deal_close_time = ( datetime )org_datetime_array[ index ]; trade_result = org_result_array[ index ]; // Now check if the same time already exists in the output array: current_time = ( datetime )group_datetime_array[ real_trades ]; if( current_time > 0 && MathAbs( current_time - deal_close_time ) > 0.0 ) { real_trades++; // move the pointer to the next element group_result_array[ real_trades ] = trade_result; group_datetime_array[ real_trades ] = deal_close_time; } else { group_result_array[ real_trades ] += trade_result; group_datetime_array[ real_trades ] = deal_close_time; } } real_trades++; // now this is the number of unique elements // If there are less trades than necessary, exit: if( real_trades < _max_trades ) { return( 0 ); } if( ArraySize( last_result_array ) != _max_trades ) { ArrayResize( last_result_array, _max_trades ); ArrayResize( last_datetime_array, _max_trades ); } // Write the accumulated data to the output arrays with reversed indexation: for( index = 0; index < _max_trades; index++ ) { last_result_array[ _max_trades - 1 - index ] = group_result_array[ index ]; last_datetime_array[ _max_trades - 1 - index ] = group_datetime_array[ index ]; } // In the output array replace the results of single trades with the accumulating total: for( index = 1; index < _max_trades; index++ ) { last_result_array[ index ] += last_result_array[ index - 1 ]; } return( _max_trades ); }
Başlangıçta - bir iş sembolü belirtilmişse ve giriş parametreleri doğruysa - zorunlu kontroller yapılır.
Daha sonra, belirtilen tarihten şu ana kadar olan yatırımların ve talimatların geçmişini okuyoruz. Bu, kodun aşağıdaki bölümünde gerçekleştirilir:
// Request the history of deals and orders from the specified time to the current moment: if( HistorySelect( monitoring_begin_date, TimeCurrent( )) != true ) { return( 0 ); } // Calculate number of trades: count = HistoryDealsTotal( ); // If there are less trades in the history than it is necessary, then exit: if( count < _max_trades ) { return( 0 ); }
Ayrıca, geçmişteki toplam yatırım sayısı kontrol edilir. Belirtilenden daha azsa, diğer eylemler anlamsızdır. "İşlenmemiş" diziler hazırlanır hazırlanmaz, bunları alım satım işlemi geçmişinden gelen bilgilerle doldurma döngüsü yürütülür. Bu, şu şekilde yapılır:
// Fill the "raw" array from the base of history of trades: real_trades = 0; for( index = count - 1; index >= limit; index-- ) { deal_ticket = HistoryDealGetTicket( index ); // If the trades are not closed, don't go further: deal_entry = HistoryDealGetInteger( deal_ticket, DEAL_ENTRY ); if( deal_entry != DEAL_ENTRY_OUT ) { continue; } // Check "magic number" of deal if necessary: deal_magic = HistoryDealGetInteger( deal_ticket, DEAL_MAGIC ); if( _magic != 0 && deal_magic != _magic ) { continue; } // Check symbols of deal: deal_symbol = HistoryDealGetString( deal_ticket, DEAL_SYMBOL ); if( symbol != deal_symbol ) { continue; } // Check type of deal if necessary: deal_type = HistoryDealGetInteger( deal_ticket, DEAL_TYPE ); if( _type != -1 && deal_type != _type ) { continue; } else if( _type == -1 && ( deal_type != DEAL_TYPE_BUY && deal_type != DEAL_TYPE_SELL )) { continue; } // Check time of closing of deal: deal_close_time = ( datetime )HistoryDealGetInteger( deal_ticket, DEAL_TIME ); if( deal_close_time < monitoring_begin_date ) { continue; } // So, we can rad another trade: org_datetime_array[ real_trades ] = deal_close_time / 60; org_result_array[ real_trades ] = HistoryDealGetDouble( deal_ticket, DEAL_PROFIT ) / HistoryDealGetDouble( deal_ticket, DEAL_VOLUME ); real_trades++; } // If there are less trades than necessary, exit: if( real_trades < _max_trades ) { return( 0 ); }
Başlangıçta, geçmişteki yatırım bileti HistoryDealGetTicket işlevi kullanılarak okunur, yatırım ayrıntılarının daha fazla okunması, elde edilen bilet kullanılarak gerçekleştirilir. Yalnızca kapalı alım satım işlemleriyle ilgilendiğimiz için (bakiyeyi analiz edeceğiz), önce yatırımın türü kontrol edilir. Bu, DEAL_ENTRY parametresi ile HistoryDealGetInteger işlevi çağrılarak yapılır. İşlev DEAL_ENTRY_OUT öğesini döndürürse, bir pozisyonu kapatıyor demektir.
Bunun ardından yatırımın "sihirli sayısı", yatırımın türü (yöntemin giriş parametresi belirtilir) ve yatırımın sembolü kontrol edilir. Yatırımın tüm parametreleri gereklilikleri karşılıyorsa son parametre kontrol edilir - yatırımın kapanış zamanı. Bu, şu şekilde yapılır:
// Check the time of closing of deal: deal_close_time = ( datetime )HistoryDealGetInteger( deal_ticket, DEAL_TIME ); if( deal_close_time < monitoring_begin_date ) { continue; }
Yatırımın tarihi/saati, geçmişi izlemenin başlangıç tarihi/saati ile karşılaştırılır. Yatırımın tarihi/saati verilenden büyükse, o zaman alım satım işlemimizi diziye okumaya gideriz - Alım satım işleminin sonucunu puan cinsinden ve alım satım işleminin saatini dakika cinsinden okuyun (bu durumda, kapanış zamanı). Bundan sonra, real_trades yatırımlarını okuma sayacı artar ve döngü devam eder.
"İşlenmemiş" diziler gerekli miktarda bilgi ile doldurulduktan sonra, yatırımların kapanış zamanının saklandığı diziyi sıralamalıyız. Aynı zamanda, org_datetime_array dizisindeki kapanış zamanının ve org_result_array dizisindeki yatırımların sonuçlarının yazışmalarını tutmamız gerekiyor. Bu, özel olarak yazılmış yöntem kullanılarak yapılır:
TBalanceHistory::SortMasterSlaveArray( double& _master[ ], double& _slave[ ] ). İlk parametre _master - artan şekilde sıralanan dizidir. İkinci parametre _slave - öğelerinin birinci dizinin öğeleriyle eşzamanlı olarak taşınması gereken dizidir. Sıralama "kabarcık" yöntemiyle yapılır.
Yukarıda açıklanan tüm işlemlerden sonra, zamana ve zamana göre sıralanmış yatırımların sonuçlarını içeren iki diziye sahip oluruz. Denge eğrisi üzerinde yalnızca bir nokta (Y ekseni üzerindeki nokta) zamanın her anına (X eksenindeki nokta) karşılık gelebileceği için dizinin öğelerini aynı kapanış zamanı ile (varsa) gruplandırmamız gerekir. Bu işlemi, kodun aşağıdaki kısmı gerçekleştirir:
// Fill the output array with grouped data ( group by identity of date/time of closing of position ): real_trades = 0; for( index = 0; index < count; index++ ) { // Get another trade: deal_close_time = ( datetime )org_datetime_array[ index ]; trade_result = org_result_array[ index ]; // Now check, if the same time already exists in the output array: current_time = ( datetime )group_datetime_array[ real_trades ]; if( current_time > 0 && MathAbs( current_time - deal_close_time ) > 0.0 ) { real_trades++; // move the pointer to the next element group_result_array[ real_trades ] = trade_result; group_datetime_array[ real_trades ] = deal_close_time; } else { group_result_array[ real_trades ] += trade_result; group_datetime_array[ real_trades ] = deal_close_time; } } real_trades++; // now this is the number of unique elements
Pratik olarak, "aynı" kapanış zamanına sahip tüm alım satım işlemleri burada toplanır. Sonuçlar, TBalanceHistory::group_datetime_array (kapanış zamanı) ve TBalanceHistory::group_result_array (alım satım işlemlerinin sonuçları) dizilerine yazılır. Bunun ardından, benzersiz öğelere sahip iki sıralı dizi elde ederiz. Bu durumda zamanın kimliği bir dakika içinde kabul edilir. Bu dönüşüm, grafiksel olarak gösterilebilir:
Şekil 3. Yatırımları aynı zamana göre gruplandırma
Bir dakika içindeki tüm yatırımlar (şekildeki sol kısım), zamanın yuvarlanması ve sonuçların toplanmasıyla (şekildeki sağ kısım) tek bir işlemde gruplandırılır. Bu, yatırımların kapanış zamanının "yüksek perdeden çıkan sesini" yumuşatmaya ve düzenlemenin istikrarını iyileştirmeye olanak tanır.
Bundan sonra, elde edilen dizilerin iki dönüşümünü daha yapmanız gerekir. En erken yatırımın sıfır öğeye karşılık gelmesi için öğelerin sırasını tersine çevirin ve tekli alım satım işlemlerinin sonuçlarını kümülatif toplamla, yani bakiyeyle değiştirin. Bu, kodun aşağıdaki parçasında yapılır:
// Write the accumulated data into output arrays with reversed indexation: for( index = 0; index < _max_trades; index++ ) { last_result_array[ _max_trades - 1 - index ] = group_result_array[ index ]; last_datetime_array[ _max_trades - 1 - index ] = group_datetime_array[ index ]; } // Replace the results of single trades with the cumulative total in the output array: for( index = 1; index < _max_trades; index++ ) { last_result_array[ index ] += last_result_array[ index - 1 ]; }
TBalanceSlope Sınıfı
Bu sınıf, bir hesabın denge eğrisi ile işlem yapmak için tasarlanmıştır. TBalanceHistory sınıfından oluşturulur ve tüm korumalı ve herkese açık verilerini ve yöntemlerini devralır. Yapısına ayrıntılı bir şekilde göz atalım:
//--------------------------------------------------------------------- // Operations with the balance curve: //--------------------------------------------------------------------- class TBalanceSlope : public TBalanceHistory { private: double current_slope; // current angle of slope of the balance curve int slope_count_points; // number of points ( trades ) for calculation of slope angle private: double LR_koeff_A, LR_koeff_B; // rates for the equation of the straight-line regression double LR_points_array[ ]; // array of point of the straight-line regression private: void CalcLR( double& X[ ], double& Y[ ] ); // calculate the equation of the straight-line regression public: void SetSlopePoints( int _number ); // set the number of points for calculation of angle of slope double CalcSlope( ); // calculate the slope angle public: void TBalanceSlope( ); // constructor void ~TBalanceSlope( ); // destructor };
Denge eğrisi üzerinde belirtilen puan (alım satım işlemleri) miktarı için çizilen doğrusal regresyon çizgisinin eğim açısı ile denge eğrisinin eğim açısını belirleyeceğiz. Bu nedenle, her şeyden önce, aşağıdaki formun doğrusal regresyon denklemini hesaplamamız gerekir: A*x + B. Bu iş, aşağıdaki yöntemle gerçekleştirilir:
//--------------------------------------------------------------------- // Calculate the equation of the straight-line regression: //--------------------------------------------------------------------- // input parameters: // X[ ] - arras of values of number series on the X axis; // Y[ ] - arras of values of number series on the Y axis; //--------------------------------------------------------------------- void TBalanceSlope::CalcLR( double& X[ ], double& Y[ ] ) { double mo_X = 0, mo_Y = 0, var_0 = 0, var_1 = 0; int i; int size = ArraySize( X ); double nmb = ( double )size; // If the number of points is less than two, the curve cannot be calculated: if( size < 2 ) { return; } for( i = 0; i < size; i++ ) { mo_X += X[ i ]; mo_Y += Y[ i ]; } mo_X /= nmb; mo_Y /= nmb; for( i = 0; i < size; i++ ) { var_0 += ( X[ i ] - mo_X ) * ( Y[ i ] - mo_Y ); var_1 += ( X[ i ] - mo_X ) * ( X[ i ] - mo_X ); } // Value of the A coefficient: if( var_1 != 0.0 ) { LR_koeff_A = var_0 / var_1; } else { LR_koeff_A = 0.0; } // Value of the B coefficient: LR_koeff_B = mo_Y - LR_koeff_A * mo_X; // Fill the array of points that lie on the regression line: ArrayResize( LR_points_array, size ); for( i = 0; i < size; i++ ) { LR_points_array[ i ] = LR_koeff_A * X[ i ] + LR_koeff_B; } }
Burada, ilk verilere göre regresyon çizgisinin minimum pozisyon hatasını hesaplamak için en küçük kareler yöntemini kullanıyoruz. Hesaplanan doğru üzerinde bulunan Y koordinatlarını saklayan dizi de doldurulur. Bu dizi şu anda kullanılmamaktadır ve daha fazla geliştirme amaçlıdır.
Verilen sınıfta kullanılan ana yöntem şu şekildedir: TBalanceSlope::CalcSlope. Bu, belirtilen son alım satım işlemi miktarı ile hesaplanan denge eğrisinin eğim açısını döndürür. Bunun ne şekilde gerçekleştiğine bakalım:
//--------------------------------------------------------------------- // Calculate slope angle: //--------------------------------------------------------------------- double TBalanceSlope::CalcSlope( ) { // Get result of trading from the history of trades: int nmb = GetTradeResultsArray( slope_count_points ); if( nmb < slope_count_points ) { return( 0.0 ); } // Calculate the regression line by the results of last trades: CalcLR( last_datetime_array, last_result_array ); current_slope = LR_koeff_A; return( current_slope ); }
İlk olarak, denge eğrisinin belirlenen son noktalarının miktarı analiz edilir. Bu, TBalanceSlope::GetTradeResultsArray temel sınıfının yöntemi çağrılarak yapılır. Okuma noktalarının miktarı belirtilenden az değilse regresyon çizgisi hesaplanır. Bu, TBalanceSlope::CalcLR yöntemi kullanılarak yapılır. Bir önceki adımda doldurulan, temel sınıfa ait last_result_array ve last_datetime_array dizileri argümanlar olarak kullanılır.
Geri kalan yöntemler basittir ve ayrıntılı bir açıklama gerektirmez.
TBalanceSlopeControl Sınıfı
İş hacmini değiştirerek denge eğrisinin eğimini yöneten temel sınıftır. TBalanceSlope sınıfından oluşturulur ve tüm genel ve korumalı yöntemlerini ve verilerini devralır. Bu sınıfın tek amacı, denge eğrisinin mevcut eğim açısına bağlı olarak mevcut iş hacmini hesaplamaktır. Şimdi bunu ayrıntılı bir şekilde inceleyelim:
//--------------------------------------------------------------------- // Managing slope of the balance curve: //--------------------------------------------------------------------- enum LotsState { LOTS_NORMAL = 1, // mode of trading with normal volume LOTS_REJECTED = -1, // mode of trading with lowered volume LOTS_INTERMEDIATE = 0, // mode of trading with intermediate volume }; //--------------------------------------------------------------------- class TBalanceSlopeControl : public TBalanceSlope { private: double min_slope; // slope angle that corresponds to the mode of volume rejection double max_slope; // slope angle that corresponds to the mode of normal volume double centr_slope; // slope angle that corresponds to the mode of volume switching without hysteresis private: ControlType control_type; // type of the regulation function private: double rejected_lots; // volume in the rejection mode double normal_lots; // volume in the normal mode double intermed_lots; // volume in the intermediate mode private: LotsState current_lots_state; // current mode of volume public: void SetControlType( ControlType _control ); // set type of the regulation characteristic void SetControlParams( double _min_slope, double _max_slope, double _centr_slope ); public: double CalcTradeLots( double _min_lots, double _max_lots ); // get trade volume protected: double CalcIntermediateLots( double _min_lots, double _max_lots, double _slope ); public: void TBalanceSlopeControl( ); // constructor void ~TBalanceSlopeControl( ); // destructor };
Mevcut hacmi hesaplamadan önce, başlangıç parametrelerini ayarlamamız gerekir. Bu, aşağıdaki yöntemler çağrılarak yapılır:
void SetControlType( ControlType _control ); // set type of the regulation characteristic
Giriş parametresi_control - bu, düzenleme özelliğinin türüdür. Aşağıdaki değere sahip olabilir:
- STEP_WITH_HYSTERESISH - Histerezisli kademeli düzenleme özelliği,
- STEP_WITHOUT_HYSTERESIS - Histerezissiz kademeli düzenleme özelliği,
- LINEAR - Doğrusal düzenleme özelliği,
- NON_LINEAR - Doğrusal olmayan düzenleme özelliği (bu sürümde uygulanmamıştır).
void SetControlParams( double _min_slope, double _max_slope, double _centr_slope );
Giriş parametreleri şu şekildedir:
- _min_slope - Minimum hacimle alım satıma karşılık gelen denge eğrisinin eğim açısı,
- _max_slope - Maksimum hacimle alım satıma karşılık gelen denge eğrisinin eğim açısı,
- _centr_slope - Histerezissiz kademeli düzenleme özelliğine karşılık gelen denge eğrisinin eğim açısı.
Hacim, aşağıdaki yöntem kullanılarak hesaplanır:
//--------------------------------------------------------------------- // Get trade volume: //--------------------------------------------------------------------- double TBalanceSlopeControl::CalcTradeLots( double _min_lots, double _max_lots ) { // Try to calculate slope of the balance curve: double current_slope = CalcSlope( ); // If the specified amount of trades is not accumulated yet, trade with minimal volume: if( GetRealTrades( ) < GetSlopePoints( )) { current_lots_state = LOTS_REJECTED; rejected_lots = trade_symbol.NormalizeLots( _min_lots ); return( rejected_lots ); } // If the regulation function is stepped without hysteresis: if( control_type == STEP_WITHOUT_HYSTERESIS ) { if( current_slope < centr_slope ) { current_lots_state = LOTS_REJECTED; rejected_lots = trade_symbol.NormalizeLots( _min_lots ); return( rejected_lots ); } else { current_lots_state = LOTS_NORMAL; normal_lots = trade_symbol.NormalizeLots( _max_lots ); return( normal_lots ); } } // If the slope of linear regression for the balance curve is less than the allowed one: if( current_slope < min_slope ) { current_lots_state = LOTS_REJECTED; rejected_lots = trade_symbol.NormalizeLots( _min_lots ); return( rejected_lots ); } // If the slope of linear regression for the balance curve is greater than specified: if( current_slope > max_slope ) { current_lots_state = LOTS_NORMAL; normal_lots = trade_symbol.NormalizeLots( _max_lots ); return( normal_lots ); } // The slope of linear regression for the balance curve is within specified borders (intermediate state): current_lots_state = LOTS_INTERMEDIATE; // Calculate the value of intermediate volume: intermed_lots = CalcIntermediateLots( _min_lots, _max_lots, current_slope ); intermed_lots = trade_symbol.NormalizeLots( intermed_lots ); return( intermed_lots ); }
TBalanceSlopeControl::CalcTradeLots yönteminin uygulanmasının başlıca önemli noktaları şu şekildedir:
- Belirtilen minimum alım satım işlemi miktarı birikene kadar, minimum hacimle alım satım yapın. Bu mantıklıdır; zira Expert Advisor'ın alım satım işlemi için ayarladıktan hemen sonra hangi dönemde (karlı veya değil) olduğu bilinmiyor.
- Düzenleme işlevi histerezissiz bir adımsa alım satım modları arasında geçiş açısını TBalanceSlopeControl::SetControlParams yöntemiyle ayarlamak için yalnızca _centr_slope parametresini kullanmalısınız. _min_slope ve _max_slope parametreleri yok sayılır. Bu, MetaTrader 5 strateji test cihazında bu parametre ile doğru optimizasyonu gerçekleştirmek için yapılır.
Hesaplanan eğim açısına bağlı olarak minimum, maksimum veya ara hacimde alım satım yapılır. Ara hacim basit yöntemle hesaplanır - TBalanceSlopeControl::CalcIntermediateLots. Bu yöntem korumalıdır ve sınıf içinde kullanılır. Kodu aşağıda gösterilmiştir:
//--------------------------------------------------------------------- // Calculation of intermediate volume: //--------------------------------------------------------------------- double TBalanceSlopeControl::CalcIntermediateLots( double _min_lots, double _max_lots, double _slope ) { double lots; // If the regulation function is stepped with hysteresis: if( control_type == STEP_WITH_HYSTERESISH ) { if( current_lots_state == LOTS_REJECTED && _slope > min_slope && _slope < max_slope ) { lots = _min_lots; } else if( current_lots_state == LOTS_NORMAL && _slope > min_slope && _slope < max_slope ) { lots = _max_lots; } } // If the regulation function is linear: else if( control_type == LINEAR ) { double a = ( _max_lots - _min_lots ) / ( max_slope - min_slope ); double b = normal_lots - a * .max_slope; lots = a * _slope + b; } // If the regulation function is non-linear ( not implemented yet ): else if( control_type == NON_LINEAR ) { lots = _min_lots; } // If the regulation function is unknown: else { lots = _min_lots; } return( lots ); }
Bu sınıfın diğer yöntemleri herhangi bir açıklama gerektirmez.
Sistemi Expert Advisor'a Gömme Örneği
Denge eğrisinin eğimini kontrol eden sistemin uygulama sürecini adım adım bir Expert Advisor'da ele alalım.
Adım 1 - Geliştirilen kitaplığı Expert Advisor'a bağlama talimatının eklenmesi:
#include <BalanceSlopeControl.mqh>
Adım 2 - Denge çizgisinin eğimini kontrol eden sistemin parametrelerinin ayarlanması için harici değişkenleri Expert Advisor'a ekleme:
//--------------------------------------------------------------------- // Parameters of the system of controlling the slope of the balance curve; //--------------------------------------------------------------------- enum SetLogic { No = 0, Yes = 1, }; //--------------------------------------------------------------------- input SetLogic UseAutoBalanceControl = No; //--------------------------------------------------------------------- input ControlType BalanceControlType = STEP_WITHOUT_HYSTERESIS; //--------------------------------------------------------------------- // Amount of last trades for calculation of LR of the balance curve: input int TradesNumberToCalcLR = 3; //--------------------------------------------------------------------- // Slope of LR to decrease the volume to minimum: input double LRKoeffForRejectLots = -0.030; //--------------------------------------------------------------------- // Slope of LR to restore the normal mode of trading: input double LRKoeffForRestoreLots = 0.050; //--------------------------------------------------------------------- // Slope of LR to work in the intermediate mode: input double LRKoeffForIntermedLots = -0.020; //--------------------------------------------------------------------- // Decrease the initial volume to the specified value when the LR is inclined down input double RejectedLots = 0.10; //--------------------------------------------------------------------- // Normal work volume in the mode of MM with fixed volume: input double NormalLots = 1.0;
Adım 3 - TBalanceSlopeControl türünün nesnesini Expert Advisor'a ekleme:
TBalanceSlopeControl BalanceControl;
Bu bildirim, Expert Advisor'ın başına, işlev tanımlarından önce eklenebilir.
Adım 4 - Expert Advisor'ın OnInit işlevine denge eğrisi kontrol sisteminin başlatılması için kodu ekleme:
// Adjust our system of controlling the slope of the balance curve: BalanceControl.SetTradeSymbol( Symbol( )); BalanceControl.SetControlType( BalanceControlType ); BalanceControl.SetControlParams( LRKoeffForRejectLots, LRKoeffForRestoreLots, LRKoeffForIntermedLots ); BalanceControl.SetSlopePoints( TradesNumberToCalcLR ); BalanceControl.SetFiltrParams( 0, -1, 0 ); BalanceControl.SetMonitoringBeginDate( 0 );
Adım 5 - Expert Advisor'ın OnTick işlevine mevcut piyasa bilgilerini yenilemek için yöntem çağrısını ekleme:
// Refresh market information:
BalanceControl.RefreshSymbolInfo( );
Bu yöntemin çağrısı, OnTick işlevinin en başına veya gelen yeni çubuğun kontrolünden sonra (bu kontrole sahip Expert Advisor'lar için) eklenebilir.
Adım 6 - Pozisyonların açıldığı koddan önce mevcut hacim hesaplama kodunu ekleme:
if( UseAutoBalanceControl == Yes ) { current_lots = BalanceControl.CalcTradeLots( RejectedLots, NormalLots ); } else { current_lots = NormalLots; }
Expert Advisor'da bir Para Yönetimi sistemi kullanılıyorsa o zaman NormalLots yerine TBalanceSlopeControl::CalcTradeLots yöntemini yazmalısınız - Expert Advisor'ın PY sistemi tarafından hesaplanan mevcut hacim.
Yukarıda açıklanan yerleşik sisteme sahip Test Expert Advisor BSCS-TestExpert.mq5 bu makaleye eklenmiştir. Bunun çalışma prensibi, CCI göstergesinin düzeylerinin kesişimine dayanmaktadır. Bu Expert Advisor, test için geliştirilmiştir ve gerçek hesaplar üzerinde çalışmak için uygun değildir. Bunu, EURUSD'nin H4 zaman diliminde (01.07.2008 - 01.09.2010) test edeceğiz.
Bu EA'nın çalışmasının sonucunu analiz edelim. Eğimi kontrol etme sistemi devre dışıyken denge değişimi grafiği aşağıda gösterilmiştir. Bunu görmek için, UseAutoBalanceControl harici parametresi için No değerini ayarlayın.
Şekil 4. İlk denge değişimi grafiği
Şimdi UseAutoBalanceControl harici parametresini Yes olarak ayarlayın ve Expert Advisor'ı test edin. Denge eğimini kontrol etmek için etkinleştirilmiş sistemle grafiği alacaksınız.
Şekil 5. Kontrol sistemi etkinken denge değişimi grafiği
Üst grafikte (şek.4) çoğu dönemin kesildiği gibi göründüğünü, alt grafikte (şek.5) ise düz bir forma sahip olduğunu görebilirsiniz. Bu, sistemimizin çalışmasının sonucudur. Expert Advisor'ın çalışmasının ana parametrelerini karşılaştırabilirsiniz:
Parametre | UseAutoBalanceControl = Hayır | UseAutoBalanceControl = Evet |
---|---|---|
Toplam net kar: | 18 378,00 | 17 261,73 |
Kar faktörü: | 1,47 | 1,81 |
Kurtarma faktörü: | 2,66 | 3,74 |
Beklenen kazanç: | 117,81 | 110,65 |
Mutlak bakiye düşüşü: | 1 310,50 | 131,05 |
Mutlak hisse senedi düşüşü: | 1 390,50 | 514,85 |
Maksimum bakiye düşüşü: | 5 569,50 (%5,04) | 3 762,15 (%3,35) |
Maksimum hisse senedi düşüşü: | 6 899,50 (%6,19) | 4 609,60 (%4,08) |
Karşılaştırılanlar arasında en iyi olan parametreler yeşil renkle vurgulanır. Kar ve beklenen kazanç biraz azaldı; bu, iş hacmi durumları arasındaki geçiş gecikmelerinin bir sonucu olarak ortaya çıkan düzenlemenin diğer yüzüdür. Sonuç olarak, Expert Advisor'ın çalışma oranlarında bir iyileşme vardır. Özellikle de, düşüş ve kar faktöründe bir iyileşme mevcuttur.
Sonuç
Bu sistemi iyileştirmenin birkaç yolunu görüyorum:- Expert Advisor elverişsiz bir çalışma dönemine girdiğinde sanal alım satımı kullanmak. O zaman normal iş hacmi artık önemli olmayacak. Düşüşün azaltılmasına izin verecektir.
- Expert Advisor'ın mevcut çalışma durumunu belirlemek için daha karmaşık algoritmalar kullanmak (karlı veya karlı olmayan). Örneğin, böyle bir analiz için bir nöron ağı uygulamayı deneyebiliriz. Bu durumda elbette ek incelemeye ihtiyaç vardır.
Böylece, bir Expert Advisor'ın kalite özelliklerinin iyileştirilmesine olanak sağlayan sistemin çalışma prensibini ve sonucunu göz önünde bulundurduk. Para yönetimi sistemi ile ortak çalışma, bazı durumlarda, risk artırılmadan karlılığın artırılmasına olanak tanır.
Size bir kez daha hatırlatıyorum: Hiçbir yardımcı sistem, kaybeden bir sistemden karlı bir Expert Advisor yapamaz.
MetaQuotes Ltd tarafından Rusçadan çevrilmiştir.
Orijinal makale: https://www.mql5.com/ru/articles/145
- Ü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