English Русский Deutsch 日本語
preview
MetaTrader 5'te L1 Trend Filtrelemeyi Uygulama

MetaTrader 5'te L1 Trend Filtrelemeyi Uygulama

MetaTrader 5Örnekler |
19 8
MetaQuotes
MetaQuotes
L1 trend filtrelemenin ana amacı, bir zaman serisinden altta yatan trendi şu özellikleri sağlayacak şekilde çıkarmaktır:
  • Verilerin uzun vadeli dinamikleri korunur;
  • Kısa vadeli dalgalanmalar ve gürültü bastırılır;
  • Yapısal kırılma noktaları (trend eğimindeki değişiklikler) otomatik olarak tespit edilir.
Klasik yumuşatma yöntemlerinin aksine, bu yaklaşım trendin yumuşatılmasını zorunlu kılmaz; bunun yerine, özellikle finansal zaman serisi analizi için önemli olan parçalı doğrusal bir yakınlaşma oluşturur.

L1 trend filtreleme



İçindekiler


Giriş

Finansal zaman serileri yüksek gürültü seviyeleri, sıkça görülen aykırı değerler ve değişen piyasa rejimleri ile karakterizedir. Pratikte alım-satım sistemlerinde bu durum basit ve ölçülebilir bir şekilde ortaya çıkar: klasik "yumuşatma" filtreleri (hareketli ortalamalar, HP) geriden gelir, eğim değişikliklerinin anlarını bulanıklaştırır ve genellikle yerel düzeltmeleri geri dönüşler olarak yorumlar - sonuç olarak yanlış giriş/çıkışların sayısı artar, Kar Faktörü azalır ve düşüş büyür. Ek olarak, λ düzenlileştirme parametresinin seçimi genellikle manuel ayarlamaya indirgenir ve enstrümanlar, zaman dilimleri ve geçmiş uzunlukları arasında iyi aktarılmaz.

Bu makale, bu sorunlara L1 trend filtrelemeye dayalı pratik bir çözüm önermektedir: ikinci farkların L1 düzenlileştirmesi ile optimizasyon, otomatik olarak net kırılma noktalarına sahip parçalı doğrusal bir yakınlaşma oluşturur. Temel avantajlar, kırılma noktalarının rejim değişiklikleri olarak net bir şekilde yorumlanması, λmax'ı hesaplayarak ve λ = coef · λmax göreceli parametresine geçerek düzenlileştirme ölçeğini ayarlama yeteneği ve MQL5'te uygulamaya uygun doğrusal hesaplama karmaşıklığıdır.

Yalnızca teoriyi değil, aynı zamanda eksiksiz bir pratik yol haritası da sunuyoruz: λmax ve L1 trendini hesaplama metotları, üç gösterge (trend, eğim, eğim işareti), yedi L1 trendi volatilite göstergesi, Uzman Danışmanlara entegrasyon ve tekrarlanabilir bir test protokolü (dört filtreleme modu, bakiye/varlık dışa aktarımı ve görselleştirme).


1. Trend Filtreleme Probleminin Formülasyonu

İki bileşenin toplamı olarak temsil edilen bir skaler zaman serisini ele alıyoruz:

 

Burada trend bileşeni, ise bir gürültü veya düzensiz bir bileşendir.

Amaç, gözlemlenen verilerden trendini hesaplamaktır.

Problem, orijinal verilere bağlılık ile hesaplanan trendin yumuşaklığı arasında bir dengeleme olarak ifade edilebilir.


1.1. Hodrick-Prescott Filtresi

Hodrick-Prescott filtresi, trendi minimizasyon probleminin çözümü olarak tanımlar:

Burada λ parametresi yumuşatma derecesini kontrol eder.

HP filtresinin temel özellikleri:

  • Verilere göre doğrusallık;
  • Hesaplama karmaşıklığı O(n);
  • λ küçük olduğunda, trend orijinal verilere yaklaşır;
  • λ büyük olduğunda, trend en iyi doğrusal yaklaşmaya yönelir.

Ancak, HP filtresi her zaman yumuşak bir trend üretir ve eğimdeki keskin değişiklikleri zayıf bir şekilde algılar.


1.2. L1 Trend Filtreleme Yöntemi

L1 trend filtrelemenin ana fikri, orijinal verilere yakın olan ancak eğimde mümkün olduğunca az değişiklik içeren bir trend bulmaktır. Karesel eğriliği en aza indiren klasik yumuşatma yöntemlerinin aksine, L1 yaklaşımı ikinci farkların mutlak değerlerinin toplamını en aza indirir.

Bu da temelde farklı bir sonuca yol açmaktadır:

  • İkinci farkların çoğu sıfıra eşit olur,
  • Trend otomatik olarak doğrusal segmentlere ayrılır.

Dolayısıyla, L1 filtresi trendi yumuşak hale getirmeye çalışmaz, bunun yerine gözlemlenen verileri açıklayan minimum sayıda yapısal değişikliği bulur. Bu da yöntemi, dinamiklerin genellikle yarı doğrusal yükseliş ve düşüş aşamalarından oluştuğu finansal zaman serileri için özellikle uygun hale getirmektedir.

L1 trend filtrelemede, ikinci farklar üzerindeki karesel ceza L1 normu ile değiştirilir ve trend bir dışbükey optimizasyon probleminin çözümü olarak tanımlanır:


Matris formunda:


Tanımlamalar:

  • y - girdi zaman serisi;
  • x - hesaplanan trend;
  • D - ikinci fark matrisi;
  • λ>=0 - düzenlileştirme parametresi.

L1 normunun kullanılması temelde farklı bir sonuca yol açar: birçok ikinci fark sıfır olur, bu da trendin parçalı doğrusal olduğu anlamına gelir.

İkinci fark şu şekilde tanımlanır:

ise, noktaları düz bir çizgi üzerinde yer alır.

Bu nedenle, sıfır ikinci fark trendin doğrusal bir bölümüne karşılık gelirken, sıfır olmayan ikinci fark bir kırılma noktasına karşılık gelir. L1 normu, Dx vektöründe seyrekliği destekler, yani çoğu ikinci fark sıfır olur. Bu, ilgili aralıklar boyunca trendin doğrusal olduğu anlamına gelir. İkinci farkların sıfır olmadığı noktalar trend kırılma noktaları olarak yorumlanır.

Böylece, L1 Trend Filtreleme yöntemi, trendi otomatik olarak yapısal değişiklik noktalarıyla birbirine bağlanan bir dizi doğrusal segment olarak oluşturur.

L1 trend filtrelemenin temel özellikleri:

    • Trend doğrusal segmentlerden oluşur;
    • Kırılma noktaları, zaman serisindeki yapısal değişiklikler olarak yorumlanır;
    • λ = 0 olduğunda, trend orijinal verilerle çakışır;
    • λ yeterince büyük olduğunda, trend tam olarak en iyi doğrusal yaklaşma haline gelir;
    • Hesaplama karmaşıklığı gözlem sayısında doğrusal kalır.


    1.3. Düzenlileştirme Parametresi λ'nın Rolü

    λ parametresi, yaklaşma doğruluğu ile trend karmaşıklığı arasındaki dengeyi kontrol eder:

    λ'nın değeri Çözümün niteliği
    λ=0
    x=y, yumuşatma yok
    Küçük λ
    Zayıf yumuşatma, birçok kırılma noktası
    Orta λ
    Parçalı doğrusal trend
    Büyük λ
    Neredeyse doğrusal trend
    λ≥λmax
    Tamamen doğrusal trend

    Tablo 1. L1 trendinin düzenlileştirme parametresi λ'ya bağımlılığı

    Böylece, λ trend kırılma noktalarının sayısını ve konumlarını kontrol eder.


    1.4. Problemin Geometrik Yorumu

    İstenen trend x, n boyutlu bir uzayda bir nokta olarak düşünülebilir. Yaklaşma doğruluğundan sorumlu olan amaç fonksiyonunun ilk terimi, y gözlem noktasını merkez alan bir Öklid topu tanımlar: x, y'ye ne kadar yakınsa hata o kadar küçük olur.

    İkinci farkların L1 normu ile düzenlileştirme terimi, dışbükey bir çokyüzlü küme (polihedron) tanımlar. L2 düzenlileştirmede ortaya çıkan düzgün elipsoidlerin aksine, bu çokyüzlünün keskin köşeleri vardır. Bu köşeler, trendin bazı ikinci farklarının sıfıra eşit olduğu durumlara karşılık gelir.

    Seyrek çözümlere yol açan tam da L1 normunda keskin köşelerin varlığıdır: optimum çözüm, sadece bazı kısıtlamaların aktif olduğu çokyüzlünün bir tepe noktasında yer alma eğilimindedir. Bu, çoğu ikinci farkın sıfır olduğu ve trendin otomatik olarak parçalı doğrusal bir form aldığı anlamına gelir.

    Optimum çözüm, Öklid topu ile L1 çokyüzlüsü arasındaki ilk temas noktasına karşılık gelir. Bu noktada trend, sınırlı sayıda kırılma noktasında birbirine bağlanan doğrusal segmentlerden oluşur.

    λmax parametresi, Öklid topunun L1 çokyüzlüsüne bir tepe noktasında değil, doğrusal fonksiyonların alt uzayı boyunca temas ettiği duruma karşılık gelir. Bu durumda, tüm ikinci farklar sıfırdır ve trend tam anlamıyla doğrusaldır.

    λ ≥ λmax olduğunda, L1 kısıtlamalarının hiçbiri aktif hale gelmez, bu nedenle düzenlileştirmedeki daha fazla artış çözümü değiştirmez ve trend doğrusal kalır.

    1.5. λmax'ı Hesaplama Algoritması

    N uzunluğundaki bir girdi vektörü y için maksimum düzenlileştirme parametresi λmax'ın hesaplanmasını ele alalım.

    1. (N-2)×N büyüklüğünde ikinci fark matrisi D'yi oluşturalım:


    2. Dy eğrilik vektörünü hesaplayalım.

    3. Doğrusal denklem sistemini çözelim:

    4. v vektörünün maksimum (mutlak değere göre) elemanını alalım:

    Finansal zaman serileri için λmax parametresi önemli bir pratik anlama sahiptir:

    • Düzenlileştirme parametresinin normalleştirilmesine olanak sağlar;
    • λ seçimini veri ölçeğinden bağımsız hale getirir;
    • Farklı zaman serileri arasında karşılaştırma yapmayı kolaylaştırır;
    • λ'nın maksimum düzenlileştirmenin bir kesri olarak yorumlanmasına olanak tanır.

    λ=coef_lambda_max⋅λmax şeklinde göreceli bir parametre kullanmak, burada coef_lambda_max ∈ (0, 1), pratik uygulamayı büyük ölçüde basitleştirir.

    Aşağıdaki gösterge ve Uzman Danışman örneklerinde, λ, λmax biriminde kullanılacak, parametre ayarları ise coef_lambda_max çarpanını belirtecektir.


    2. L1 Trendinin Hesaplanması için MQL5 Metotları

    L1 trend filtrelemenin pratik kullanımı için double ve float türünde vektörler için iki metot uygulanmaktadır.

    • L1TrendFilterLambdaMax maksimum düzenlileştirme parametresini hesaplar;
    • L1TrendFilter, λmax biriminde de belirtilebilen λ düzenlileştirme parametresinin belirli bir değeri için L1 trendini hesaplar.

    2.1. L1TrendFilterLambdaMax

    Bir veri vektörü için maksimum düzenlileştirme parametresi λmax'ı hesaplama metodu.

    vector<double> için hesaplama:

    bool  vector::L1TrendFilterLambdaMax(
       double          &lambda_max       // the maximum value of the regularization parameter lambda
       )
    vector<float> için hesaplama:
    bool  vectorf::L1TrendFilterLambdaMax(
       float           &lambda_max       // the maximum value of the regularization parameter lambda
       );

    Parametreler

    lambda

    [out] Düzenlileştirme parametresi λmax'ın maksimum değeri veya hata durumunda -1.

    Geri dönüş değeri

    Başarılı olursa true geri döndürür.

    Not

    Bellek tüketimi vektör büyüklüğüyle doğrusal olarak artar.


    2.2. L1TrendFilter

    Bir veri vektörü için L1 trendini hesaplama metodu.

    vector<double> için hesaplama:

    bool  vector::L1TrendFilter(
       double          lambda,         // regularization parameter
       bool            relative,       // flag indicating lambda is in λmax units
       vector&         result          // output vector with L1 filtering result
       );

    vector<float> için hesaplama:

    bool  vectorf::L1TrendFilter(
       float           lambda,         // regularization parameter
       bool            relative,       // flag indicating lambda is in λmax units
       vectorf&        result          // output vector with L1 filtering result
       );

    Parametreler

    lambda

    [in] Düzenlileştirme parametresi lambda'nın değeri (relative = true ise lambda, λmax'ın kesri olarak [0, 1] aralığında tanımlanır).

    relative

    [in] λ'nın nasıl belirtildiğini gösteren bayrak. True ise, λ, λmax biriminde belirtilir; aksi takdirde, mutlak değer kullanılır.

    result

    [out] L1 filtreleme sonucunu içeren vektör.

    Geri dönüş değeri

    Başarılı olursa true geri döndürür.

    Not

    Bellek tüketimi vektör büyüklüğüyle doğrusal olarak artar.


    λ için önerilen aralıklar (relative modu).

    λ çarpanı Sonuç
    0.005 – 0.015 neredeyse L2, gürültülü
    0.02 – 0.04 mikro segmentler
    0.04 – 0.07 sinyaller için en uygun
    0.07 – 0.12 orta vadeli trendler
    0.12 – 0.25 piyasa rejimleri
    > 0.3 birkaç segment

    Tablo 2. λmax birimi cinsinden λ çalışma aralıkları


    Pratikteki uygulamalar için 0.04-0.25 aralığındaki çarpanların kullanılması tavsiye edilir.



    3. Uygulama Örnekleri

    Bu bölümde, simüle edilmiş Brown hareketi verileri ve S&P 500 fiyat verileri üzerinde L1 trend hesaplamalarını ele alıyoruz ve hem Brown hareketi hem de FOREX piyasası verileri için λmax'ın ölçeklendirme özelliklerini inceliyoruz.

    Ayrıca, belirli semboller ve zaman dilimleri için en iyi L1 trend ayrışmasını elde etmek için optimum düzenlileştirme parametrelerinin (λmax çarpanları) belirlenmesine yardımcı olan üç gösterge varyantı sunuyoruz.

    Ayrıca, Moving Average, MACD, ADX ve EMA stratejileri için işlem sinyallerini filtreleme sonuçları da (L1 trendiyle uyum) sunulmuştur.


    3.1. Simüle Edilmiş Veriler (Rastgele Yürüyüş) Üzerinde L1 Trendinin Hesaplanması

    Örnek olarak, L1 trendini simüle edilmiş Brown hareketi verileri üzerinde λ düzenlileştirme parametresinin farklı değerleri ile hesaplamayı ele alalım.

    Komut dosyası kodu:

    //+------------------------------------------------------------------+
    //|                                                  TestL1Trend.mq5 |
    //|                             Copyright 2000-2026, MetaQuotes Ltd. |
    //|                                              http://www.mql5.com |
    //+------------------------------------------------------------------+
    #property copyright "Copyright 2000-2026, MetaQuotes Ltd."
    #property link      "https://www.mql5.com"
    #property version   "1.00"
    #property script_show_inputs
    #include <Graphics\Graphic.mqh>
    //+------------------------------------------------------------------+
    //| Generate Brown movement data                                     |
    //+------------------------------------------------------------------+
    void BMData(vector<double> &data,int &data_count)
      {
       data.Resize(data_count);
       data[0] = 0.0;
       for(int i=1; i<data_count; i++)
          data[i] = data[i-1] + (MathRand()/32767.0 - 0.5);
      }
    //+------------------------------------------------------------------+
    //| CopyValues                                                       |
    //+------------------------------------------------------------------+
    bool CopyValues(vector<double> &data_v,double &data[])
      {
       int data_count=(int)data.Size();
       if(data_count==0)
          return(false);
       ArrayResize(data,data.Size());
       for(int i=0; i<data_count; i++)
          data[i]=data_v[i];
       return(true);
      }
    //+------------------------------------------------------------------+
    //| Script program start function                                    |
    //+------------------------------------------------------------------+
    void OnStart()
      {
       MathSrand(1);
       int data_count=1000;
       vector<double> data_test;
       BMData(data_test,data_count);
    //--- prepare arrays for chart
       double x[],y[];
       ArrayResize(x,data_count);
       ArrayResize(y,data_count);
       for(int i=0; i<data_count; i++)
          x[i]=i;
    //---
       CGraphic graphic;
       long chart=0;
       string name="test";
       if(ObjectFind(chart,name)<0)
          graphic.Create(chart,name,0,0,0,1000,600);
       else
          graphic.Attach(chart,name);
       graphic.BackgroundMain("L1 Trend filtering (random walk) with different lambda");
       graphic.BackgroundMainSize(16);
       graphic.HistoryNameWidth(60);
       graphic.HistoryColor(ColorToARGB(clrGray,255));
       graphic.XAxis().AutoScale(false);
       graphic.XAxis().Min(0);
       graphic.XAxis().Max(data_count);
    //---
       CopyValues(data_test,y);
       graphic.CurveAdd(x,y,CURVE_LINES,"Data").LinesWidth(1);
    //--- L1TrendFilterLambdaMax
       double lambda_max=0.0;
       if(data_test.L1TrendFilterLambdaMax(lambda_max))
          PrintFormat("lambda_max=%f",lambda_max);
    //---
       vector<double> data_l1;
       const double lambda_factors[]= {1.0,0.9,0.8,0.5,0.25,0.1,0.01,0.05,0.001,0.0005};
       for(int i=0; i<ArraySize(lambda_factors); i++)
         {
          double lambda=lambda_max*lambda_factors[i];
          PrintFormat("%d. lambda=%f",i+1,lambda);
          bool res=data_test.L1TrendFilter(lambda_factors[i],true,data_l1);
          if(res)
            {
             CopyValues(data_l1,y);
             graphic.CurveAdd(x,y,CURVE_LINES,"lambda="+DoubleToString(lambda,0)).LinesWidth(3);
            }
         }
    //---
       graphic.CurvePlotAll();
       graphic.Update();
       DebugBreak();
      }
    //+------------------------------------------------------------------+
    
    Çıktı:
    TestL1Trend (EURUSD,H1) lambda_max=51703.353749
    TestL1Trend (EURUSD,H1) 1. lambda=51703.353749
    TestL1Trend (EURUSD,H1) 2. lambda=46533.018374
    TestL1Trend (EURUSD,H1) 3. lambda=41362.682999
    TestL1Trend (EURUSD,H1) 4. lambda=25851.676874
    TestL1Trend (EURUSD,H1) 5. lambda=12925.838437
    TestL1Trend (EURUSD,H1) 6. lambda=5170.335375
    TestL1Trend (EURUSD,H1) 7. lambda=517.033537
    TestL1Trend (EURUSD,H1) 8. lambda=2585.167687
    TestL1Trend (EURUSD,H1) 9. lambda=51.703354
    TestL1Trend (EURUSD,H1) 10. lambda=25.851677
    

    Bu örnekte, λ düzenlileştirme parametresinin azaltılmasının trend segmentlerine daha ayrıntılı bir ayrışma sağladığı görülebilir (Şekil 1).

    Eğer λ ≥ λmax ise, çözüm doğrusal regresyona (global trend) karşılık gelen düz bir çizgi haline gelir.


    Şekil 1. Brown hareketi verileri üzerinde λ'nın farklı değerleri ile L1 filtresi hesaplama örneği

    Şekil 1. Brown hareketi verileri üzerinde λ'nın farklı değerleri ile L1 filtresi hesaplama örneği


    L1 trendini hesaplamaya yönelik fonksiyonlar hem double hem de float vektörler için kullanılabilir.

    Hesaplamaların karşılaştırılması için test komut dosyası aşağıda sunulmuştur.

    //+------------------------------------------------------------------+
    //|                                       TestL1TrendFloatDouble.mq5 |
    //|                             Copyright 2000-2026, MetaQuotes Ltd. |
    //|                                              http://www.mql5.com |
    //+------------------------------------------------------------------+
    #property copyright "Copyright 2000-2026, MetaQuotes Ltd."
    #property link      "https://www.mql5.com"
    #property version   "1.00"
    
    #include <Graphics\Graphic.mqh>
    
    uint32_t ExtSeed=1;
    //+------------------------------------------------------------------+
    //| Generate Brown movement data                                     |
    //+------------------------------------------------------------------+
    template<typename T>
    void BMData(vector<T> &data,uint64_t data_count)
      {
       MathSrand(ExtSeed);
    
       data.Resize(data_count);
       data[0] = 0.0;
    
       for(uint64_t i=1; i<data_count; i++)
          data[i] = data[i-1] + T(MathRand()/32767.0 - 0.5);
      }
    //+------------------------------------------------------------------+
    //| CopyValues                                                       |
    //+------------------------------------------------------------------+
    template<typename T>
    bool CopyValues(double &data[],const vector<T> &data_v)
      {
       if(ArrayResize(data,data.Size())!=data.Size())
          return(false);
       for(uint64_t i=0; i<data.Size(); i++)
          data[i]=data_v[i];
    
       return(true);
      }
    //+------------------------------------------------------------------+
    //| L1TrendCalculate                                                 |
    //+------------------------------------------------------------------+
    template<typename T>
    bool L1TrendCalculate(double &result[],uint64_t data_count,double lambda,bool lambda_is_relative)
      {
       vector<T> data_test;
       BMData(data_test,data_count);
    
       vector<T> vres;
       if(!data_test.L1TrendFilter((T)lambda,lambda_is_relative,vres))
          return(false);
       if(ArrayResize(result,(uint32_t)vres.Size())!=vres.Size())
          return(false);
       for(uint64_t n=0; n<result.Size(); n++)
          result[n]=vres[n];
    
       return(true);
      }
    //+------------------------------------------------------------------+
    //| TestRun                                                          |
    //+------------------------------------------------------------------+
    bool TestRun(uint32_t data_count,uint32_t mode)
      {
    //--- create graph
       CGraphic graphic;
       long     chart=0;
       string   name="L1TrendTest";
    
       if(ObjectFind(chart,name)<0)
          graphic.Create(chart,name,0,0,0,1280,600);
       else
          graphic.Attach(chart,name);
    
       string mode_name="(";
       if((mode&1)==1)
          mode_name+="DOUBLE";
       if((mode&3)==3)
          mode_name+=" & ";
       if((mode&2)==2)
          mode_name+="FLOAT";
       mode_name+=")";
    
       graphic.BackgroundMain("L1Trend filtering (random walk) with different lambda "+mode_name);
       graphic.BackgroundMainSize(16);
       graphic.HistoryNameWidth(60);
       graphic.HistoryColor(ColorToARGB(clrGray,255));
       graphic.XAxis().AutoScale(false);
       graphic.XAxis().Min(0);
       graphic.XAxis().Max(data_count);
    //--- prepare arrays
       double x[];
       double y[];
    
       if(ArrayResize(x,data_count)!=data_count)
          return(false);
      
       for(uint32_t i=0; i<data_count; i++)
          x[i]=i;
    
       vector<double> v;
       BMData(v,data_count);
       v.Swap(y);
       graphic.CurveAdd(x,y,CURVE_LINES,"Data").LinesWidth(1);
    //--- calculate
       const double lambda_factors[]= {1.0,0.9,0.8,0.5,0.25,0.1,0.01,0.05,0.001,0.0005};
    //--- double
       if((mode&1)==1)
         {
          for(uint64_t i=0; i<lambda_factors.Size(); i++)
            {
             if(L1TrendCalculate<double>(y,data_count,lambda_factors[i],true))
                graphic.CurveAdd(x,y,CURVE_LINES,"DBL="+DoubleToString(lambda_factors[i],4)).LinesWidth(4);
            }
         }
    //--- float
       if((mode&2)==2)
         {
          for(uint64_t i=0; i<lambda_factors.Size(); i++)
            {
             if(L1TrendCalculate<float>(y,data_count,(float)lambda_factors[i],true))
                graphic.CurveAdd(x,y,CURVE_LINES,"FLT="+DoubleToString(lambda_factors[i],4)).LinesWidth(2);
            }
         }
    //--- update
       graphic.CurvePlotAll();
       graphic.Update();
       return(true);
      }
    //+------------------------------------------------------------------+
    //| Script program start function                                    |
    //+------------------------------------------------------------------+
    void OnStart()
      {
       for(uint32_t n=0; !IsStopped(); n++,Sleep(1000))
         {
          TestRun(1000,1+n%3);
    
          if((n%3)==2)
             ExtSeed++;
         }
      }
    //+------------------------------------------------------------------+

    Çıktı:


    3.2. S&P 500 Fiyat Serisi için L1 Trendinin Hesaplanması

    "l_1 Trend Filtering, S.J. Kim, K. Koh, S. Boyd, and D. Gorinevsky, SIAM Review, problems and techniques section, 51(2):339–360, May 2009" orijinal makalesinden log(S&P 500) hesaplamasına bakalım. 

    Komut dosyasını çalıştırmak için "snp500.txt" dosyasındaki veriler kullanılır. Şu klasöre yerleştirilmelidir: terminal_veri_klasörü\MQL5\Files

    //+------------------------------------------------------------------+
    //|                                       TestL1TrendFilterSP500.mq5 |
    //|                             Copyright 2000-2026, MetaQuotes Ltd. |
    //|                                              http://www.mql5.com |
    //+------------------------------------------------------------------+
    #property copyright "Copyright 2000-2026, MetaQuotes Ltd."
    #property link      "https://www.mql5.com"
    #property version   "1.00"
    #property script_show_inputs
    #include <Graphics\Graphic.mqh>
    //+------------------------------------------------------------------+
    //| LoadData                                                         |
    //+------------------------------------------------------------------+
    void LoadData(string filename,vector<double> &data,int &data_count)
      {
       data_count=0;
       ResetLastError();
       int file_handle=FileOpen(filename,FILE_READ|FILE_TXT|FILE_ANSI);
       if(file_handle!=INVALID_HANDLE)
         {
          while(!FileIsEnding(file_handle))
            {
             string str=FileReadString(file_handle);
             if(data.Size()<=(ulong)data_count)
                data.Resize(data_count+1);
             data[data_count]=StringToDouble(str);
             data_count++;
            }
          FileClose(file_handle);
         }
       else
          PrintFormat("Failed to open %s file, Error code = %d",filename,GetLastError());
    //---
       data.Resize(data_count);
      }
    //+------------------------------------------------------------------+
    //| Script program start function                                    |
    //+------------------------------------------------------------------+
    void OnStart()
      {
       long chart=0;
       string name="log SP500";
       int data_count=0;
       vector<double> data_sp500;
       LoadData("snp500.txt",data_sp500,data_count);
       vector<double> data_l1_sp500;
       data_l1_sp500.Resize(data_count);
    //--- L1TrendFilterLambdaMax
       double lambda_max=0.0;
       if(data_sp500.L1TrendFilterLambdaMax(lambda_max))
          PrintFormat("Lambda_max=%f",lambda_max);
       double lambda=50;
    //--- L1TrendFilter
       if(data_sp500.L1TrendFilter(lambda,false,data_l1_sp500))
         {
          //--- prepare arrays for chart
          double x[],y[],y2[];
          ArrayResize(x,data_count);
          ArrayResize(y,data_count);
          ArrayResize(y2,data_count);
          for(int i=0; i<data_count; i++)
            {
             x[i]=i;
             y[i]=data_sp500[i];
             y2[i]=data_l1_sp500[i];
            }
          //---
          CGraphic graphic;
          if(ObjectFind(chart,name)<0)
             graphic.Create(chart,name,0,0,0,1000,600);
          else
             graphic.Attach(chart,name);
          graphic.BackgroundMain("log SP500 L1 trend filtering");
          graphic.BackgroundMainSize(16);
          graphic.HistoryNameWidth(60);
          graphic.HistoryColor(ColorToARGB(clrGray,255));
          graphic.XAxis().AutoScale(false);
          graphic.XAxis().Min(0);
          graphic.XAxis().Max(data_count);
          graphic.XAxis().DefaultStep(100);
          graphic.CurveAdd(x,y,CURVE_LINES,"SP500").LinesWidth(1);
          graphic.CurveAdd(x,y2,CURVE_LINES,"L1 trend").LinesWidth(3);
          graphic.CurvePlotAll();
          graphic.Update();
          DebugBreak();
         }
      }
    //+------------------------------------------------------------------+
    

    Komut dosyasının yürütülmesinin sonucu Şekil 2'de gösterilmiştir.

    Şekil 2. S&P 500 endeksinin log fiyat serisi için L1 trendini hesaplama örneği

    Şekil 2. S&P 500 endeksinin log fiyat serisi için L1 trendini hesaplama örneği


    Uzmanlar sekmesinde, söz konusu zaman serisi için λmax değeri görüntülenecektir:

    TestL1TrendFilterSP500 (EURUSD,H1)      Lambda_max=37394.835512
    

    Bu komut dosyası, L1TrendFilterLambdaMax ve L1TrendFilter metotlarının, yöntemin yazarlarının orijinal makalesinde olduğu gibi sabit bir λ = 50 değeri ile kullanımını göstermektedir.

    Aşağıdaki örneklerde, λ düzenlileştirme parametresinin mutlak değerleri yerine, relative = true bayrağı ile göreceli değerler (λmax biriminde) kullanılacaktır.


    3.3. λmax'ın Ölçekleme Özellikleri

    λmax parametresi, çözümün global bir doğrusal yaklaşmaya dönüştüğü düzenlileştirmenin üst sınırını tanımladığı için L1 filtrelemede önemli bir rol oynar. Bu niceliğin ilginç bir özelliği, zaman serisinin uzunluğuna bağlı olarak ölçeklenmesidir.

    Sayısal deneyler, λmax'ın gözlem sayısına uygun olarak bir kuvvet yasasına göre arttığını göstermektedir:

    Tanımlamalar: T - zaman serisinin uzunluğu, α - ölçeklendirme üssü.

    Rastgele bir yürüyüş (Brown hareketi) için, üssün α ≈ 2.5'e yakın olması gerektiği gösterilebilir. Brown hareketinin genliği olarak artarken, ikinci fark operatörü olarak ölçeklenir.  λmax hesaplanırken, serinin eğriliğinin integraliyle ilgili bir niceliğin maksimum değeri etkin bir şekilde değerlendirilir.


    Sonuç olarak, birleşik ölçeklendirme şu ilişkiye yol açar:

    Bu da α ≈ 2.5 üssüne karşılık gelir.

    Dolayısıyla, zaman serisinin uzunluğu arttıkça, λmax değeri doğrusal bir artıştan çok daha hızlı bir şekilde artar.


    3.3.1. Brown Hareketi için Sayısal Deney

    Ölçekleme yasasını doğrulamak için sayısal bir deney yapıldı.

    Farklı T zaman serisi uzunlukları için Brown hareketinin gerçekleşmeleri oluşturuldu ve ardından λmax'ın ortalama değeri hesaplandı.

    Logaritmik bir yaklaşma kullanıldı:

    Bu da α üssünün doğrusal regresyon kullanılarak hesaplanmasını sağlar.

    Deney için kod aşağıda verilmiştir.

    //+------------------------------------------------------------------+
    //|                            TestScalingLambdaMaxBrownMovement.mq5 |
    //|                             Copyright 2000-2026, MetaQuotes Ltd. |
    //|                                              http://www.mql5.com |
    //+------------------------------------------------------------------+
    #property copyright "Copyright 2000-2026, MetaQuotes Ltd."
    #property link      "https://www.mql5.com"
    #property version   "1.00"
    #include <Graphics\Graphic.mqh>
    //+------------------------------------------------------------------+
    //| Generate Brownian motion                                         |
    //+------------------------------------------------------------------+
    void GenerateBrownian(int N,vector<double> &data)
      {
       data.Resize(N);
       data[0] = 0.0;
       for(int i=1; i<N; i++)
          data[i] = data[i-1] + (MathRand()/32767.0 - 0.5);
      }
    //+------------------------------------------------------------------+
    //| LinearRegression                                                 |
    //+------------------------------------------------------------------+
    void LinearRegression(const double &x[], const double &y[], int n, double &a, double &b)
      {
       double sx = 0.0, sy = 0.0, sxx = 0.0, sxy = 0.0;
       for(int i = 0; i < n; i++)
         {
          sx  += x[i];
          sy  += y[i];
          sxx += x[i] * x[i];
          sxy += x[i] * y[i];
         }
       double denom = n * sxx - sx * sx;
       a = (n * sxy - sx * sy) / denom;
       b = (sy - a * sx) / n;
      }
    //+------------------------------------------------------------------+
    //| TestScaling with statistics                                      |
    //+------------------------------------------------------------------+
    void TestScalingStatistics()
      {
       MathSrand(42);
       int RUNS = 10;     //
       int MC   = 10;   // Monte Carlo
       double alpha_values[];
       ArrayResize(alpha_values, RUNS);
    // --- geometric grid of T
       int nT = 8;
       int Tvals[];
       ArrayResize(Tvals, nT);
       int T0 = 64;
       for(int i = 0; i < nT; i++)
          Tvals[i] = T0 << i;
       Print("Scaling test with statistics");
    //---
       double logT[];
       double logLambda[];
       vector<double> bm;
       vector<double> l1_trend;
       for(int run = 0; run < RUNS; run++)
         {
          ArrayResize(logT, nT);
          ArrayResize(logLambda, nT);
          //---
          for(int i = 0; i < nT; i++)
            {
             int T = Tvals[i];
             double lambda_sum = 0.0;
             l1_trend.Resize(T);
             for(int k = 0; k < MC; k++)
               {
                GenerateBrownian(T, bm);
                double lambda_max=0.0;
                if (bm.L1TrendFilterLambdaMax(lambda_max))
                   lambda_sum += lambda_max;
                bm.L1TrendFilter(0.2,true,l1_trend);
               }
             double lambda_avg = lambda_sum / MC;
             logT[i]      = MathLog((double)T);
             logLambda[i] = MathLog(lambda_avg);
            }
          // --- regression
          double alpha, c;
          LinearRegression(logT, logLambda, nT, alpha, c);
          alpha_values[run] = alpha;
          PrintFormat("run %d -> alpha = %.6f", run+1, alpha);
         }
    //--- statistics
       double mean = 0.0;
       for(int i=0;i<RUNS;i++)
          mean += alpha_values[i];
       mean /= RUNS;
    // --- standard deviation
       double var = 0.0;
       for(int i=0;i<RUNS;i++)
          var += (alpha_values[i]-mean)*(alpha_values[i]-mean);
       var /= (RUNS - 1);
       double stddev = MathSqrt(var);
    // --- standard error of mean
       double sem = stddev / MathSqrt((double)RUNS);
    // --- theoretical comparison
       double alpha_theory=2.5;
       double percent_error=MathAbs(mean-alpha_theory)/alpha_theory*100.0;
    //--- results
       PrintFormat("mean alpha = %.6f", mean);
       PrintFormat("std deviation = %.6f", stddev);
       PrintFormat("standard error = %.6f", sem);
       PrintFormat("theory = %.4f", alpha_theory);
       PrintFormat("percent error from theory = %.4f %%", percent_error);
      }
    //+------------------------------------------------------------------+
    //| TestScaling                                                      |
    //+------------------------------------------------------------------+
    void TestScaling()
      {
       MathSrand(1);
    // --- geometric grid of T
       int nT = 8;
       int Tvals[];
       ArrayResize(Tvals,nT);
    //---
       int T0 = 64;
       for(int i=0; i<nT; i++)
          Tvals[i]=T0<<i;   // 64 * 2^i
    //---
       double logT[], logLambda[];
       ArrayResize(logT,nT);
       ArrayResize(logLambda,nT);
    //---
       Print("scaling test for lambda_max");
       for(int i=0; i<nT; i++)
         {
          int T = Tvals[i];
          //--- Monte-Carlo simulations
          int MC=1000;
          double lambda_sum = 0.0;
          for(int k=0; k<MC; k++)
            {
             vector<double> bm;
             GenerateBrownian(T, bm);
             double lambda_max=0.0;
             if(bm.L1TrendFilterLambdaMax(lambda_max))
                lambda_sum += lambda_max;
            }
          double lambda_avg=lambda_sum/MC;
          logT[i]= MathLog((double)T);
          logLambda[i]=MathLog(lambda_avg);
          PrintFormat("T=%5d   <lambda_max>=%.6f",T,lambda_avg);
         }
    // --- linear regression in log-log
       double alpha, c;
       LinearRegression(logT,logLambda,nT,alpha,c);
    //---
       PrintFormat("estimated scaling exponent alpha = %.4f",alpha);
       double alpha_theory=2.5;
       PrintFormat("theoretical value = %.4f",alpha_theory);
    //--- plot scaling law
       CGraphic g;
       g.Create(0, "ScalingLaw",0,0,0,1000,600);
       g.BackgroundMain("Scaling law of lambda_max (Brownian motion)");
       g.BackgroundMainSize(16);
       g.CurveAdd(logT, logLambda, CURVE_POINTS, "Simulation");
    //---
       double xfit[2], yfit[2];
       xfit[0] = logT[0];
       xfit[1] = logT[nT-1];
    //---
       yfit[0] = alpha*xfit[0] + c;
       yfit[1] = alpha*xfit[1] + c;
    //---least squares fit
       g.CurveAdd(xfit, yfit, CURVE_LINES, "LS fit");
       g.CurvePlotAll();
       g.Update();
       DebugBreak();
      }
    //+------------------------------------------------------------------+
    //| Script program start function                                    |
    //+------------------------------------------------------------------+
    void OnStart()
      {
    //--- calculate scaling with statistics
       TestScalingStatistics();
    //--- show sample results
       TestScaling();
      }
    //+------------------------------------------------------------------+

    Çıktı:

    TestScalingLambdaMaxBrownMovement (EURUSD,H1)   Scaling test with statistics    
    TestScalingLambdaMaxBrownMovement (EURUSD,H1)   run 1 -> alpha = 2.480774       
    TestScalingLambdaMaxBrownMovement (EURUSD,H1)   run 2 -> alpha = 2.530977       
    TestScalingLambdaMaxBrownMovement (EURUSD,H1)   run 3 -> alpha = 2.435511       
    TestScalingLambdaMaxBrownMovement (EURUSD,H1)   run 4 -> alpha = 2.461984       
    TestScalingLambdaMaxBrownMovement (EURUSD,H1)   run 5 -> alpha = 2.467093       
    TestScalingLambdaMaxBrownMovement (EURUSD,H1)   run 6 -> alpha = 2.487965       
    TestScalingLambdaMaxBrownMovement (EURUSD,H1)   run 7 -> alpha = 2.532371       
    TestScalingLambdaMaxBrownMovement (EURUSD,H1)   run 8 -> alpha = 2.455831       
    TestScalingLambdaMaxBrownMovement (EURUSD,H1)   run 9 -> alpha = 2.483485       
    TestScalingLambdaMaxBrownMovement (EURUSD,H1)   run 10 -> alpha = 2.420283      
    TestScalingLambdaMaxBrownMovement (EURUSD,H1)   mean alpha = 2.475627   
    TestScalingLambdaMaxBrownMovement (EURUSD,H1)   std deviation = 0.036281        
    TestScalingLambdaMaxBrownMovement (EURUSD,H1)   standard error = 0.011473       
    TestScalingLambdaMaxBrownMovement (EURUSD,H1)   theory = 2.5000 
    TestScalingLambdaMaxBrownMovement (EURUSD,H1)   percent error from theory = 0.9749 %    
    TestScalingLambdaMaxBrownMovement (EURUSD,H1)   scaling test for lambda_max     
    TestScalingLambdaMaxBrownMovement (EURUSD,H1)   T=   64   <lambda_max>=97.302362        
    TestScalingLambdaMaxBrownMovement (EURUSD,H1)   T=  128   <lambda_max>=566.626861       
    TestScalingLambdaMaxBrownMovement (EURUSD,H1)   T=  256   <lambda_max>=3162.076116      
    TestScalingLambdaMaxBrownMovement (EURUSD,H1)   T=  512   <lambda_max>=18271.204936     
    TestScalingLambdaMaxBrownMovement (EURUSD,H1)   T= 1024   <lambda_max>=100057.796790    
    TestScalingLambdaMaxBrownMovement (EURUSD,H1)   T= 2048   <lambda_max>=578620.887399    
    TestScalingLambdaMaxBrownMovement (EURUSD,H1)   T= 4096   <lambda_max>=3192555.936035   
    TestScalingLambdaMaxBrownMovement (EURUSD,H1)   T= 8192   <lambda_max>=17895314.647170  
    TestScalingLambdaMaxBrownMovement (EURUSD,H1)   estimated scaling exponent alpha = 2.4967       
    TestScalingLambdaMaxBrownMovement (EURUSD,H1)   theoretical value = 2.5000      
    

    Çift log grafiği (çift logaritmik ölçekte), Brown hareketi için λmax fonksiyonunun veri noktası sayısına bağlı olarak bir kuvvet yasası ilişkisi gösterdiğini yansıtmaktadır.

    Şekil 3. Brown hareketi için LambdaMax'ın kuvvet yasası bağımlılığı

    Şekil 3. Brown hareketi için LambdaMax'ın kuvvet yasası bağımlılığı


    Simülasyon sonuçları şunları göstermektedir:

    mean alpha = 2.4756
    std deviation = 0.036
    theory = 2.5
    percent error ≈ 1%

    Dolayısıyla, deney teorik ilişkiyi doğruluyor:



    Çift logaritmik ölçekteki çift log grafiği log(λmax) ve log(T) arasında doğrusal bir ilişki olduğunu ortaya koymaktadır.


    3.3.2. Finansal Zaman Serileri için Ölçeklendirme

    Benzer bir deney FOREX piyasası fiyat serileri için de gerçekleştirildi. Farklı döviz pariteleri ve zaman dilimleri için α üssü hesaplandı.

    Sonuçlar, gerçek finansal veriler için α değerinin de α ≈ 2.45-2.60 aralığında olduğunu ve bunun Brown hareketi için teorik değere çok yakın olduğunu göstermektedir. Bu, λmax'ın ölçekleme davranışının neredeyse evrensel olduğu ve farklı piyasalar ve zaman dilimlerinde geçerli olduğu anlamına gelir.

    TestScalingLambdaMaxSymbol.mq5 komut dosyası, M1-H1 standart zaman dilimlerinde belirli bir sembol için λmax üssünü hesaplar.

    //+------------------------------------------------------------------+
    //|                                   TestScalingLambdaMaxSymbol.mq5 |
    //|                             Copyright 2000-2026, MetaQuotes Ltd. |
    //|                                              http://www.mql5.com |
    //+------------------------------------------------------------------+
    #property copyright "Copyright 2000-2026, MetaQuotes Ltd."
    #property link      "https://www.mql5.com"
    #property version   "1.00"
    #property script_show_inputs
    //--- input parameters
    input string WorkSymbol   = "EURUSD";     // Symbol
    input int    YearStart    = 2024;
    input int    YearEnd      = 2025;
    #include <Graphics\Graphic.mqh>
    //+------------------------------------------------------------------+
    //| GetHistoricalData                                                |
    //+------------------------------------------------------------------+
    bool GetHistoricalData(double &data[], string symbol, ENUM_TIMEFRAMES tf, int year_start, int year_end)
      {
       datetime from = StringToTime(IntegerToString(year_start) + ".01.01 00:00");
       datetime to   = StringToTime(IntegerToString(year_end)   + ".12.31 23:59");
       int copied = CopyClose(symbol, tf, from, to, data);
       if(copied <= 0)
         {
          Print("Error in CopyClose: ", GetLastError());
          ArrayResize(data, 0);
          return false;
         }
    //PrintFormat("Loaded bars: %d (%s %s)", ArraySize(data), symbol, EnumToString(tf));
       return true;
      }
    //+------------------------------------------------------------------+
    //| LinearRegression                                                 |
    //+------------------------------------------------------------------+
    void LinearRegression(const double &x[], const double &y[], int n, double &a, double &b)
      {
       double sx = 0, sy = 0, sxx = 0, sxy = 0;
       for(int i = 0; i < n; i++)
         {
          sx  += x[i];
          sy  += y[i];
          sxx += x[i] * x[i];
          sxy += x[i] * y[i];
         }
       double denom = n*sxx - sx*sx;
       if(denom!=0)
         {
          a = (n*sxy-sx*sy)/denom;
          b = (sy-a*sx)/n;
         }
      }
    //+------------------------------------------------------------------+
    //| Scaling test for one timeframe                                   |
    //+------------------------------------------------------------------+
    bool TestScalingLambaMaxTF(string symbol, ENUM_TIMEFRAMES tf, double &logT_out[], double &logLambda_out[], double &alpha_out)
      {
       MathSrand(42);
       double prices[];
       if(!GetHistoricalData(prices, symbol, tf, YearStart, YearEnd))
          return false;
       int Tvals[];
       int nT=8;
       int T0=64;
       ArrayResize(Tvals, nT);
       for(int i = 0; i < nT; i++)
          Tvals[i] = T0 << i;
       ArrayResize(logT_out, nT);
       ArrayResize(logLambda_out, nT);
       int data_size = ArraySize(prices);
       vector<double> data_prices;
       for(int i = 0; i < nT; i++)
         {
          int T = Tvals[i];
          int MC = 1000;
          double lambda_sum = 0.0;
          for(int k = 0; k < MC; k++)
            {
             if(data_size < T)
                break;
             int start = MathRand() % (data_size - T);
             data_prices.Resize(T);
             for(int j=0; j<T;  j++)
                data_prices[j]=prices[start+j];
             double lambda_max=0.0;
             if(data_prices.L1TrendFilterLambdaMax(lambda_max))
                lambda_sum += lambda_max;
            }
          double lambda_avg = lambda_sum / MC;
          logT_out[i]=MathLog((double)T);
          logLambda_out[i]=MathLog(lambda_avg);
          //PrintFormat("TF=%s T=%5d   <lambda_max>=%.6f", EnumToString(tf), T, lambda_avg);
         }
       double c;
       LinearRegression(logT_out, logLambda_out, nT, alpha_out, c);
       PrintFormat("%s (%s) estimated scaling exponent = %.4f", symbol,EnumToString(tf), alpha_out);
       return true;
      }
    //+------------------------------------------------------------------+
    //| TestScalingLambdaMaxSymbol                                       |
    //+------------------------------------------------------------------+
    void TestScalingLambdaMaxSymbol(string symbol)
      {
       ENUM_TIMEFRAMES timeframes[] = {PERIOD_M1, PERIOD_M2, PERIOD_M3, PERIOD_M4, PERIOD_M5, PERIOD_M6,
                                       PERIOD_M10, PERIOD_M12, PERIOD_M15, PERIOD_M20, PERIOD_M30, PERIOD_H1
                                      };
       uint colors[] = {clrRed,clrBlue,clrGreen,clrOrange,clrPurple,clrDarkGreen,clrCyan,
                        clrNavy,clrOrangeRed,clrDodgerBlue,clrCrimson,clrDarkRed
                       };
    //---
       CGraphic g;
       g.Create(0,"ScalingLawTest",0,0,0,1000,600);
       g.BackgroundMain("Scaling law of lambda_max ("+symbol+")");
       g.BackgroundMainSize(16);
       PrintFormat("%s scaling test for standard timeframes",symbol);
       for(int i = 0; i < ArraySize(timeframes); i++)
         {
          double logT[], logLambda[], alpha;
          // Print("processing timeframe: ", EnumToString(timeframes[i]), " -----");
          if(TestScalingLambaMaxTF(symbol,timeframes[i],logT,logLambda,alpha))
            {
             g.CurveAdd(logT,logLambda,ColorToARGB(colors[i % ArraySize(colors)],255),CURVE_POINTS_AND_LINES,EnumToString(timeframes[i]));
            }
         }
       g.CurvePlotAll();
       g.Update();
    //---
       DebugBreak();
      }
    //+------------------------------------------------------------------+
    //| Script program start function                                    |
    //+------------------------------------------------------------------+
    void OnStart()
      {
    //--- estimate lambda_max scale exponent for price data
       TestScalingLambdaMaxSymbol(WorkSymbol);
      }
    //+------------------------------------------------------------------+
    

    EURUSD için sonuçlar:

    TestScalingLambdMaxSymbol (EURUSD,H1)   EURUSD scaling test for standard timeframes     
    TestScalingLambdMaxSymbol (EURUSD,H1)   EURUSD (PERIOD_M1) estimated scaling exponent = 2.5038  
    TestScalingLambdMaxSymbol (EURUSD,H1)   EURUSD (PERIOD_M2) estimated scaling exponent = 2.5350  
    TestScalingLambdMaxSymbol (EURUSD,H1)   EURUSD (PERIOD_M3) estimated scaling exponent = 2.5034  
    TestScalingLambdMaxSymbol (EURUSD,H1)   EURUSD (PERIOD_M4) estimated scaling exponent = 2.5422  
    TestScalingLambdMaxSymbol (EURUSD,H1)   EURUSD (PERIOD_M5) estimated scaling exponent = 2.5341  
    TestScalingLambdMaxSymbol (EURUSD,H1)   EURUSD (PERIOD_M6) estimated scaling exponent = 2.5132  
    TestScalingLambdMaxSymbol (EURUSD,H1)   EURUSD (PERIOD_M10) estimated scaling exponent = 2.5188 
    TestScalingLambdMaxSymbol (EURUSD,H1)   EURUSD (PERIOD_M12) estimated scaling exponent = 2.5126 
    TestScalingLambdMaxSymbol (EURUSD,H1)   EURUSD (PERIOD_M15) estimated scaling exponent = 2.5208 
    TestScalingLambdMaxSymbol (EURUSD,H1)   EURUSD (PERIOD_M20) estimated scaling exponent = 2.4887 
    TestScalingLambdMaxSymbol (EURUSD,H1)   EURUSD (PERIOD_M30) estimated scaling exponent = 2.5695 
    TestScalingLambdMaxSymbol (EURUSD,H1)   EURUSD (PERIOD_H1) estimated scaling exponent = 2.6118  

    EURUSD için sonuçlar (standart zaman dilimleri M1-H1) Şekil 4'te gösterilmiştir.

    Şekil 4. Farklı EURUSD zaman dilimlerinde λmax'ın kuvvet yasası bağımlılığı

    Şekil 4. Farklı EURUSD zaman dilimlerinde λmax'ın kuvvet yasası bağımlılığı

    Benzer şekilde, diğer döviz pariteleri de analiz edilebilir.

    USDJPY için:

    TestScalingLambdMaxSymbol (EURUSD,H1)   USDJPY scaling test for standard timeframes     
    TestScalingLambdMaxSymbol (EURUSD,H1)   USDJPY (PERIOD_M1) estimated scaling exponent = 2.5851  
    TestScalingLambdMaxSymbol (EURUSD,H1)   USDJPY (PERIOD_M2) estimated scaling exponent = 2.5825  
    TestScalingLambdMaxSymbol (EURUSD,H1)   USDJPY (PERIOD_M3) estimated scaling exponent = 2.4889  
    TestScalingLambdMaxSymbol (EURUSD,H1)   USDJPY (PERIOD_M4) estimated scaling exponent = 2.5099  
    TestScalingLambdMaxSymbol (EURUSD,H1)   USDJPY (PERIOD_M5) estimated scaling exponent = 2.5059  
    TestScalingLambdMaxSymbol (EURUSD,H1)   USDJPY (PERIOD_M6) estimated scaling exponent = 2.4939  
    TestScalingLambdMaxSymbol (EURUSD,H1)   USDJPY (PERIOD_M10) estimated scaling exponent = 2.5548 
    TestScalingLambdMaxSymbol (EURUSD,H1)   USDJPY (PERIOD_M12) estimated scaling exponent = 2.5641 
    TestScalingLambdMaxSymbol (EURUSD,H1)   USDJPY (PERIOD_M15) estimated scaling exponent = 2.5525 
    TestScalingLambdMaxSymbol (EURUSD,H1)   USDJPY (PERIOD_M20) estimated scaling exponent = 2.5390 
    TestScalingLambdMaxSymbol (EURUSD,H1)   USDJPY (PERIOD_M30) estimated scaling exponent = 2.5805 
    TestScalingLambdMaxSymbol (EURUSD,H1)   USDJPY (PERIOD_H1) estimated scaling exponent = 2.4645  
    

    USDJPY için sonuçlar da kuvvet yasası ilişkisiyle iyi bir şekilde yaklaşmaktadır.

    Şekil 5. Farklı USDJPY zaman dilimlerinde λmax'ın kuvvet yasası bağımlılığı

    Şekil 5. Farklı USDJPY zaman dilimlerinde λmax'ın kuvvet yasası bağımlılığı

    GBPUSD için:

    TestScalingLambdMaxSymbol (EURUSD,H1)   GBPUSD scaling test for standard timeframes     
    TestScalingLambdMaxSymbol (EURUSD,H1)   GBPUSD (PERIOD_M1) estimated scaling exponent = 2.5235  
    TestScalingLambdMaxSymbol (EURUSD,H1)   GBPUSD (PERIOD_M2) estimated scaling exponent = 2.5449  
    TestScalingLambdMaxSymbol (EURUSD,H1)   GBPUSD (PERIOD_M3) estimated scaling exponent = 2.5439  
    TestScalingLambdMaxSymbol (EURUSD,H1)   GBPUSD (PERIOD_M4) estimated scaling exponent = 2.5427  
    TestScalingLambdMaxSymbol (EURUSD,H1)   GBPUSD (PERIOD_M5) estimated scaling exponent = 2.5248  
    TestScalingLambdMaxSymbol (EURUSD,H1)   GBPUSD (PERIOD_M6) estimated scaling exponent = 2.5308  
    TestScalingLambdMaxSymbol (EURUSD,H1)   GBPUSD (PERIOD_M10) estimated scaling exponent = 2.5293 
    TestScalingLambdMaxSymbol (EURUSD,H1)   GBPUSD (PERIOD_M12) estimated scaling exponent = 2.5235 
    TestScalingLambdMaxSymbol (EURUSD,H1)   GBPUSD (PERIOD_M15) estimated scaling exponent = 2.5069 
    TestScalingLambdMaxSymbol (EURUSD,H1)   GBPUSD (PERIOD_M20) estimated scaling exponent = 2.4977 
    TestScalingLambdMaxSymbol (EURUSD,H1)   GBPUSD (PERIOD_M30) estimated scaling exponent = 2.5659 
    TestScalingLambdMaxSymbol (EURUSD,H1)   GBPUSD (PERIOD_H1) estimated scaling exponent = 2.5524  

    Benzer bir durum GBPUSD fiyat serisi için de gözlemlenmektedir (Şekil 6).

    Şekil 6. Farklı GBPUSD zaman dilimlerinde λmax'ın kuvvet yasası bağımlılığı

    Şekil 6. Farklı GBPUSD zaman dilimlerinde λmax'ın kuvvet yasası bağımlılığı


    Dikkate alınan EURUSD, USDJPY ve GBPUSD serileri için hesaplanan üs değerleri de 2.5'e yakındır.

    Çoklu zaman dilimlerinde ve döviz paritelerinde λmax fonksiyonu için log-log ölçeğinde doğrusal ilişkiler, λmax'ın gözlem sayısına göre bir kuvvet yasası bağımlılığı gösterdiğini ortaya koymaktadır.


    3.3.3. Ölçeklendirmenin Pratik Sonuçları

    λmax için bir kuvvet yasası bağımlılığının varlığının önemli bir pratik anlamı vardır.

    λmax ∝ T^2.5 olduğundan, λ'nın mutlak değeri büyük ölçüde aşağıdakilere bağlıdır:

    1. Veri penceresinin uzunluğu,
    2. Zaman dilimi,
    3. Zaman serisinin ölçeği.

    Bu nedenle, λ'nın mutlak değerini kullanmak pratikte uygun değildir.

    Çok daha sağlam bir yaklaşım, 0<c<1 olan λ=c⋅λmax göreceli parametresini kullanmaktır.

    Böyle bir yaklaşım:

    • düzenlileştirme parametresini ölçekten bağımsız hale getirir,
    • farklı enstrümanlar arasında parametre aktarımını kolaylaştırır,
    • farklı zaman dilimlerinde aynı ayarların kullanılmasına olanak tanır.

    Dolayısıyla, sonraki tüm örneklerde λ parametresi λmax birimi cinsinden belirtilecektir.


    3.4. L1 Trendi Göstergeleri

    Bu bölümde üç tür gösterge ele alınmaktadır:

    • Kapanış fiyatlarına dayalı olarak L1 trendinin hesaplanması;
    • L1 trendinin doğrusal artış katsayılarının (eğim) hesaplanması;
    • L1 trend eğiminin işaretinin hesaplanması.
    Bu göstergeler trend ayrışmasının görsel analizi için kullanılabilir ve alım-satım stratejilerinde kullanılmak üzere λ düzenlileştirme parametresinin uygun değerlerinin belirlenmesine yardımcı olabilir.

    3.4.1. L1TrendFilter.mq5 - L1 trendi göstergesi

    Bu örnekte, L1 filtresi, λmax biriminde belirtilen lambda katsayısı ile belirli sayıda çubuk için (örnekte, BarsToShow = 1000) kapanış fiyatları kullanılarak hesaplanır.

    Hesaplama L1TrendFilter(relative = true) metot çağrısını kullanır; burada λ parametresi λmax biriminde tanımlanır. Gösterge değerleri doğrudan grafik penceresinde görüntülenir.

    L1TrendFilter.mq5 göstergesinin kodu aşağıda verilmiştir.

    //+------------------------------------------------------------------+
    //|                                                L1TrendFilter.mq5 |
    //|                             Copyright 2000-2026, MetaQuotes Ltd. |
    //|                                             https://www.mql5.com |
    //+------------------------------------------------------------------+
    #property copyright "Copyright 2000-2026, MetaQuotes Ltd."
    #property link      "https://www.mql5.com"
    #property version   "1.00"
    #property indicator_chart_window
    #property indicator_buffers 1
    #property indicator_plots   1
    //---
    #property indicator_label1  "L1TrendFilter"
    #property indicator_type1   DRAW_LINE
    #property indicator_color1  clrDodgerBlue
    #property indicator_width1  2
    //---
    input int    BarsToShow = 1000;  // Number of bars to calculate L1
    input double CoefLambda = 0.015; // Lambda in lambda_max units
    //---
    double Trend[];
    //+------------------------------------------------------------------+
    //| Indicator initialization function                                |
    //+------------------------------------------------------------------+
    int OnInit()
      {
       SetIndexBuffer(0,Trend,INDICATOR_DATA);
       ArrayInitialize(Trend,EMPTY_VALUE);
    //---
       PlotIndexSetDouble(0,PLOT_EMPTY_VALUE,EMPTY_VALUE);
       IndicatorSetInteger(INDICATOR_DIGITS,_Digits);
    //---
       return(INIT_SUCCEEDED);
      }
    //+------------------------------------------------------------------+
    //| Indicator iteration function                                     |
    //+------------------------------------------------------------------+
    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[])
      {
    //--- check bars
       static bool warned=false;
       if(rates_total < BarsToShow)
         {
          if(!warned)
            {
             Print("Waiting for enough bars: ",BarsToShow);
             warned=true;
            }
          ArrayInitialize(Trend,EMPTY_VALUE);
          return(0);
         }
    //--- check new bar
       static datetime last_bar_time=0;
       bool new_bar=(time[0]!=last_bar_time);
       bool need_recalc=(prev_calculated==0) || new_bar || (rates_total!=prev_calculated);
       if(!need_recalc)
          return(prev_calculated);
       last_bar_time=time[0];
    //--- range
       int start=rates_total-BarsToShow;
    //--- hide old bars
       for(int i=0; i<start; i++)
          Trend[i]=EMPTY_VALUE;
    //---
       int data_count=BarsToShow;
    //--- copy Close
       vector<double> DataClose;
       DataClose.Resize(data_count);
       for(int i=0; i<data_count; i++)
          DataClose[i]=close[start+i];
    //--- lambda max
       double lambda_max=0.0;
       bool res=DataClose.L1TrendFilterLambdaMax(lambda_max);
       if(res)
         {
          PrintFormat("lambda_max=%f (%s,%s) Coef=%f lambda=%f",
                      lambda_max,Symbol(),EnumToString(Period()),CoefLambda,lambda_max*CoefLambda);
         }
    //--- L1 trend filtering
       vector<double> filtered_data;
       filtered_data.Resize(data_count);
       if(DataClose.L1TrendFilter(CoefLambda,true,filtered_data))
         {
          for(int i=0; i<data_count; i++)
             Trend[start+i]=filtered_data[i];
         }
    //---
       return(rates_total);
      }
    //+------------------------------------------------------------------+
    

    Şekil 7'de, CoefLambda = 0.015 ile L1TrendFilter.mq5 göstergesinin hesaplanmasına ilişkin bir örnek gösterilmektedir.


    Şekil 7. CoefLambda = 0.015 ile L1TrendFilter.mq5 göstergesini hesaplama örneği

    Şekil 7. CoefLambda = 0.015 ile L1TrendFilter.mq5 göstergesini hesaplama örneği


    Karşılaştırma için, farklı düzenlileştirme parametreleri ile birkaç varyant hesaplanabilir.

    Şekil 8, CoefLambda = 0.015, CoefLambda = 0.025 ve CoefLambda = 0.055 parametreleri ile yapılan hesaplamaları göstermektedir.


    Şekil 8. Farklı CoefLambda değerleri ile L1TrendFilter.mq5 göstergesini hesaplama örnekleri

    Şekil 8. Farklı CoefLambda değerleri ile L1TrendFilter.mq5 göstergesini hesaplama örnekleri



    3.4.2. L1TrendFilterSlope.mq5 - L1 trend dinamiklerinin göstergesi

    Trend eğimini görüntülemek için, L1TrendFilter göstergesi değerlerinin artışı kullanılabilir.

    Örnek olarak, değerleri ayrı bir pencerede görüntüleyen L1TrendFilterSlope göstergesine bakalım.

    Göstergenin kodu aşağıda verilmiştir.

    //+------------------------------------------------------------------+
    //|                                           L1TrendFilterSlope.mq5 |
    //|                             Copyright 2000-2026, MetaQuotes Ltd. |
    //|                                             https://www.mql5.com |
    //+------------------------------------------------------------------+
    #property copyright "Copyright 2000-2026, MetaQuotes Ltd."
    #property link      "https://www.mql5.com"
    #property version   "1.00"
    #property indicator_separate_window
    #property indicator_buffers 1
    #property indicator_plots   1
    //---
    #property indicator_label1  "L1TrendFilterSlope"
    #property indicator_type1   DRAW_LINE
    #property indicator_color1  clrDodgerBlue
    #property indicator_width1  2
    //---
    input int    BarsToShow = 1000;  // Number of bars to calculate L1
    input double CoefLambda = 0.015; // Lambda in lambda_max units
    //---
    double Trend[];
    //+------------------------------------------------------------------+
    //| Indicator initialization function                                |
    //+------------------------------------------------------------------+
    int OnInit()
      {
       SetIndexBuffer(0,Trend,INDICATOR_DATA);
       ArrayInitialize(Trend,EMPTY_VALUE);
    //---
       PlotIndexSetDouble(0,PLOT_EMPTY_VALUE,EMPTY_VALUE);
       IndicatorSetInteger(INDICATOR_DIGITS,_Digits);
    //---
       return(INIT_SUCCEEDED);
      }
    //+------------------------------------------------------------------+
    //| Indicator iteration function                                     |
    //+------------------------------------------------------------------+
    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[])
      {
    //--- check bars
       static bool warned=false;
       if(rates_total < BarsToShow)
         {
          if(!warned)
            {
             Print("Waiting for enough bars: ",BarsToShow);
             warned=true;
            }
          ArrayInitialize(Trend,EMPTY_VALUE);
          return(0);
         }
    //--- check  new bar
       static datetime last_bar_time=0;
       bool new_bar=(time[0]!=last_bar_time);
       bool need_recalc= (prev_calculated==0) || new_bar || (rates_total!=prev_calculated);
       if(!need_recalc)
          return(prev_calculated);
       last_bar_time=time[0];
    //---
       int start=rates_total-BarsToShow;
       int data_count=BarsToShow;
    //--- hide old bars
       for(int i=0;i<start;i++)
          Trend[i]=EMPTY_VALUE;
    //--- copy Close
       vector<double> DataClose;
       DataClose.Resize(data_count);
       for(int i=0;i<data_count;i++)
          DataClose[i]=close[start+i];
    //--- lambda max
       double lambda_max=0.0;
       if(DataClose.L1TrendFilterLambdaMax(lambda_max))
         {
          PrintFormat("lambda_max=%f (%s,%s) Coef=%f lambda=%f",
                      lambda_max,Symbol(),EnumToString(Period()),CoefLambda,lambda_max*CoefLambda);
         }
    //--- L1 filtering
       vector<double> filtered_data;
       filtered_data.Resize(data_count);
       bool res=DataClose.L1TrendFilter(CoefLambda,true,filtered_data);
       if(res)
         {
          //--- slope (first difference)
          for(int i=1; i<data_count; i++)
            {
             double delta=filtered_data[i]-filtered_data[i-1];
             Trend[start+i]=delta;
            }
          //--- copy first element
          Trend[start]=Trend[start+1];
         }
       return(rates_total);
      }
    //+------------------------------------------------------------------+
    

    L1TrendFilter.mq5 ve L1TrendFilterSlope.mq5 göstergelerinin sonucu Şekil 9'da gösterilmiştir.

    Şekil 9. CoefLambda = 0.015 ile L1TrendFilter.mq5 ve L1TrendFilterSlope.mq5 göstergelerini hesaplama örneği

    Şekil 9. Şekil 9. CoefLambda = 0.015 ile L1TrendFilter.mq5 ve L1TrendFilterSlope.mq5 göstergelerini hesaplama örneği


    3.4.3. L1TrendFilterSlopeSign.mq5 - L1 trend yönü göstergesi

    Benzer şekilde, L1TrendFilterSlope.mq5 göstergesinin artışının işaretini görüntüleyen bir gösterge hesaplanabilir.

    L1TrendFilterSlopeSign.mq5 göstergesinin kodu:

    //+------------------------------------------------------------------+
    //|                                       L1TrendFilterSlopeSign.mq5 |
    //|                             Copyright 2000-2026, MetaQuotes Ltd. |
    //|                                             https://www.mql5.com |
    //+------------------------------------------------------------------+
    #property copyright "Copyright 2000-2026, MetaQuotes Ltd."
    #property link      "https://www.mql5.com"
    #property version   "1.00"
    #property indicator_separate_window
    #property indicator_buffers 1
    #property indicator_plots   1
    //---
    #property indicator_label1  "L1TrendFilterSlope"
    #property indicator_type1   DRAW_LINE
    #property indicator_color1  clrDodgerBlue
    #property indicator_width1  2
    //---
    input int    BarsToShow = 1000;  // Number of bars to calculate L1
    input double CoefLambda = 0.015; // Lambda in lambda_max units
    //---
    double Trend[];
    //+------------------------------------------------------------------+
    //| Signum                                                           |
    //+------------------------------------------------------------------+
    double Signum(const double value)
      {
       return((value>0)-(value<0));
      }
    //+------------------------------------------------------------------+
    //| Indicator initialization function                                |
    //+------------------------------------------------------------------+
    int OnInit()
      {
       SetIndexBuffer(0,Trend,INDICATOR_DATA);
       ArrayInitialize(Trend,EMPTY_VALUE);
    //---
       PlotIndexSetDouble(0,PLOT_EMPTY_VALUE,EMPTY_VALUE);
       IndicatorSetInteger(INDICATOR_DIGITS,_Digits);
    //---
       return(INIT_SUCCEEDED);
      }
    //+------------------------------------------------------------------+
    //| Indicator iteration function                                     |
    //+------------------------------------------------------------------+
    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[])
      {
    //--- check bars
       static bool warned=false;
       if(rates_total < BarsToShow)
         {
          if(!warned)
            {
             Print("Waiting for enough bars: ",BarsToShow);
             warned=true;
            }
          ArrayInitialize(Trend,EMPTY_VALUE);
          return(0);
         }
    //--- check new bar
       static datetime last_bar_time=0;
       bool new_bar=(time[0]!=last_bar_time);
       bool need_recalc=(prev_calculated==0) || new_bar || (rates_total!=prev_calculated);
       if(!need_recalc)
          return(prev_calculated);
       last_bar_time=time[0];
    //---
       int start=rates_total-BarsToShow;
       int data_count=BarsToShow;
    //--- hide old bars
       for(int i=0; i<start; i++)
          Trend[i]=EMPTY_VALUE;
    //--- copy Close
       vector<double> DataClose;
       DataClose.Resize(data_count);
       for(int i=0; i<data_count; i++)
          DataClose[i]=close[start+i];
    //--- lambda max
       double lambda_max=0.0;
       bool res=DataClose.L1TrendFilterLambdaMax(lambda_max);
       if(res)
         {
          PrintFormat("lambda_max=%f (%s,%s) Coef=%f lambda=%f",
                      lambda_max,Symbol(),EnumToString(Period()),CoefLambda,lambda_max*CoefLambda);
         }
    //--- L1 filtering
       vector<double> filtered_data;
       filtered_data.Resize(data_count);
       res=DataClose.L1TrendFilter(CoefLambda,true,filtered_data);
       if(res)
         {
          Trend[start]=0;
          for(int i=1; i<data_count; i++)
            {
             double delta=filtered_data[i]-filtered_data[i-1];
             Trend[start+i]=Signum(delta);
            }
         }
       return(rates_total);
      }
    //+------------------------------------------------------------------+
    

    Her üç göstergenin yer aldığı bir örnek Şekil 10'da gösterilmiştir (aynı katsayı değeri CoefLambda = 0.015 kullanılmıştır).

    Şekil 10. CoefLambda = 0.015 ile L1TrendFilter.mq5, L1TrendFilterSlope.mq5 ve L1TrendFilterSlopeSign.mq5 göstergelerini hesaplama örneği

    Şekil 10. Şekil 10. CoefLambda = 0.015 ile L1TrendFilter.mq5, L1TrendFilterSlope.mq5 ve L1TrendFilterSlopeSign.mq5 göstergelerini hesaplama örneği




    3.4.4. L1 Trendine Dayalı Volatilite Göstergeleri

    Bu bölümde, L1 trendine dayalı olarak bir finansal enstrümanın volatilitesini değerlendirmek için tasarlanmış göstergeler sunulmaktadır.

    Bu araçlar, piyasadaki istikrarsızlık ve istikrar dönemlerinin belirlenmesini, mevcut piyasa dinamiklerinin analiz edilmesini ve daha bilinçli işlem kararları verilmesini mümkün kılar.

    Bu bölümde ele alınan göstergeler şunlardır:

    • L1Volatility.mq5 - L1 trendine göre artık volatilite;
    • L1VolatilitySmoothed.mq5 - yumuşatılmış artık volatilite;
    • L1VolatilityAbsolute.mq5 - mutlak volatilite;
    • L1VolatilityNormalized.mq5 - normalleştirilmiş volatilite;
    • L1VolatilityNormalizedSmoothed.mq5 - yumuşatılmış normalleştirilmiş volatilite;
    • L1VolatilityRegime.mq5 - volatiliteye dayalı piyasa rejimi tespiti.

    Tüm göstergeler, analitik tutarlılık sağlayan ve elde edilen sonuçların yorumlanmasını basitleştiren birleşik bir L1 trendi çerçevesi üzerine inşa edilmiştir.

    Bu göstergelerin kullanımı, yüksek ve düşük volatilite dönemlerinin görsel olarak tanımlanmasına ve mevcut piyasa rejiminin - yatay seyir, trend, genişleme veya panik - belirlenmesine olanak tanır.

    Sonuç olarak, bir yatırımcı, örneğin düşük volatilite dönemlerinde daha temkinli yaklaşımlar veya güçlü piyasa hareketleri sırasında daha aktif stratejiler uygulayarak alım-satım stratejilerini mevcut piyasa koşullarına uyarlayabilir.


    3.4.4.1. L1Volatility.mq5 - L1 volatilite göstergesi

    Gösterge, artık volatiliteyi kapanış fiyatları ile L1 trendinin karşılık gelen değeri arasındaki fark olarak hesaplar.

    Bu yaklaşım, istikrarsız piyasa dönemlerinin ve kesin giriş veya çıkış anlarının belirlenmesine olanak tanır.

    Görsel olarak, gösterge ayrı bir grafik penceresinde turuncu bir çizgi olarak görüntülenir.

    Gösterge aşağıdakilere yardımcı olur:

    • L1 trendinden fiyat sapmalarını değerlendirme ve piyasa hareketinin gücünü ölçme;
    • Daha doğru risk yönetimi için yerel volatilite artışlarını tespit etme;
    • Aynı zaman dilimi içinde farklı enstrümanların dinamiklerini karşılaştırma.

    Gösterge, özellikle kısa vadeli volatilite değişikliklerinin daha geniş trend bağlamını kaybetmeden izlenmesi gereken sistemlerde kullanışlıdır.

    L1Volatility.mq5 göstergesinin kodu aşağıda verilmiştir.

    //+------------------------------------------------------------------+
    //|                                                 L1Volatility.mq5 |
    //|                             Copyright 2000-2026, MetaQuotes Ltd. |
    //|                                             https://www.mql5.com |
    //+------------------------------------------------------------------+
    #property copyright "Copyright 2000-2026, MetaQuotes Ltd."
    #property link      "https://www.mql5.com"
    #property version   "1.00"
    #property indicator_separate_window
    #property indicator_buffers 1
    #property indicator_plots   1
    //---
    #property indicator_label1  "L1Volatility"
    #property indicator_type1   DRAW_LINE
    #property indicator_color1  clrOrangeRed
    #property indicator_width1  2
    //---
    input int    BarsToShow = 1000;  // Number of bars to calculate L1
    input double CoefLambda = 0.015; // Lambda in lambda_max units
    //---
    double Volatility[];
    //---
    //+------------------------------------------------------------------+
    //| Indicator initialization function                                |
    //+------------------------------------------------------------------+
    int OnInit()
      {
       SetIndexBuffer(0,Volatility,INDICATOR_DATA);
       ArrayInitialize(Volatility,EMPTY_VALUE);
    //---
       PlotIndexSetDouble(0,PLOT_EMPTY_VALUE,EMPTY_VALUE);
       IndicatorSetInteger(INDICATOR_DIGITS,_Digits);
    //---
       return(INIT_SUCCEEDED);
      }
    //+------------------------------------------------------------------+
    //| Indicator iteration function                                     |
    //+------------------------------------------------------------------+
    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[])
      {
    //--- check bars
       static bool warned=false;
       if(rates_total<BarsToShow)
         {
          if(!warned)
            {
             Print("Waiting for enough bars: ",BarsToShow);
             warned=true;
            }
          ArrayInitialize(Volatility,EMPTY_VALUE);
          return(0);
         }
    //--- check new bar
       static datetime last_bar_time=0;
       bool new_bar=(time[0]!=last_bar_time);
       bool need_recalc=(prev_calculated==0) || new_bar || (rates_total!=prev_calculated);
       if(!need_recalc)
          return(prev_calculated);
       last_bar_time=time[0];
    //---
       int start=rates_total-BarsToShow;
       int data_count=BarsToShow;
    //--- hide old bars
       for(int i=0;i<start;i++)
          Volatility[i]=EMPTY_VALUE;
    //--- copy Close
       vector<double> DataClose;
       DataClose.Resize(data_count);
       for(int i=0; i<data_count; i++)
          DataClose[i]=close[start+i];
    //--- lambda max
       double lambda_max=0.0;
       bool res=DataClose.L1TrendFilterLambdaMax(lambda_max);
       if(res)
         {
          PrintFormat("lambda_max=%f (%s,%s) Coef=%f lambda=%f",
                      lambda_max,Symbol(),EnumToString(Period()),CoefLambda,lambda_max*CoefLambda);
         }
    //--- L1 filter
       vector<double> filtered_data;
       filtered_data.Resize(data_count);
       res=DataClose.L1TrendFilter(CoefLambda,true,filtered_data);
       if(res)
         {
          for(int i=0; i<data_count; i++)
            {
             double residual=close[start+i]-filtered_data[i];
             Volatility[start+i]=residual;
            }
         }
    //---
       return(rates_total);
      }
    //+------------------------------------------------------------------+
    

    Hesaplama sonucu Şekil 11'de gösterilmiştir.


    Şekil 11. L1Volatility.mq5 göstergesi

    Şekil 11. L1Volatility.mq5 göstergesi



    3.4.4.2. L1VolatilitySmoothed.mq5 - yumuşatılmış artık volatilite göstergesi

    Bu gösterge, Simple Moving Average'ın (SMA) uygulandığı L1Volatility'nin yumuşatılmış bir versiyonunu temsil eder.

    Yumuşatma şunları sağlar:

    • Kısa vadeli gürültü ve aykırı değerlerin azaltılması;
    • Daha net ve daha yorumlanabilir görselleştirme;
    • Volatilitedeki kalıcı değişikliklere odaklanma.

    Gösterge, örneğin uyarlanabilir alım-satım sistemlerinde veya trend ve yatay seyir piyasa aşamaları sırasında yanlış sinyalleri filtrelerken daha uzun vadeli volatilite trendlerinin değerlendirilmesini gerektiren stratejiler için kullanışlıdır.

    L1VolatilitySmoothed.mq5 göstergesinin kodu aşağıda verilmiştir.

    //+------------------------------------------------------------------+
    //|                                         L1VolatilitySmoothed.mq5 |
    //|                             Copyright 2000-2026, MetaQuotes Ltd. |
    //|                                             https://www.mql5.com |
    //+------------------------------------------------------------------+
    #property copyright "Copyright 2000-2026, MetaQuotes Ltd."
    #property link      "https://www.mql5.com"
    #property version   "1.00"
    #property indicator_separate_window
    #property indicator_buffers 1
    #property indicator_plots   1
    #property indicator_label1  "L1VolatilitySmoothed"
    #property indicator_type1   DRAW_LINE
    #property indicator_color1  clrMediumVioletRed
    #property indicator_width1  2
    //---
    input int    BarsToShow = 1000;  // Number of bars to calculate L1
    input double CoefLambda = 0.015; // Lambda in lambda_max units
    input int    SmoothPeriod = 10;  // Smooth period
    //---
    double VolSmoothed[];
    //+------------------------------------------------------------------+
    //| Indicator initialization function                                |
    //+------------------------------------------------------------------+
    int OnInit()
      {
       SetIndexBuffer(0, VolSmoothed, INDICATOR_DATA);
       ArrayInitialize(VolSmoothed, EMPTY_VALUE);
       PlotIndexSetDouble(0, PLOT_EMPTY_VALUE, EMPTY_VALUE);
       IndicatorSetInteger(INDICATOR_DIGITS, _Digits);
    //---
       return(INIT_SUCCEEDED);
      }
    //+------------------------------------------------------------------+
    //| Indicator iteration function                                     |
    //+------------------------------------------------------------------+
    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[])
      {
       if(rates_total<BarsToShow)
         {
          ArrayInitialize(VolSmoothed,EMPTY_VALUE);
          return(0);
         }
    //--- recalc only on new bar
       static datetime last_bar_time = 0;
       if(time[0] == last_bar_time && prev_calculated > 0)
          return(prev_calculated);
       last_bar_time=time[0];
    //---
       int start=rates_total-BarsToShow;
       for(int i=0; i<start; i++)
          VolSmoothed[i]=EMPTY_VALUE;
    //--- copy close prices
       vector<double> price(BarsToShow);
       for(int i=0; i<BarsToShow; i++)
          price[i] = close[start+i];
       vector<double> l1(BarsToShow);
       bool res=price.L1TrendFilter(CoefLambda,true,l1);
       if(res)
         {
          //--- calculate raw volatility
          vector<double> rawVol(BarsToShow);
          for(int i=0; i<BarsToShow; i++)
             rawVol[i]=close[start+i]-l1[i];
          //--- apply simple moving average smoothing
          for(int i=0; i<BarsToShow; i++)
            {
             double sum = 0.0;
             int count = 0;
             for(int j=MathMax(0,i-SmoothPeriod+1); j<=i; j++)
               {
                sum+=rawVol[j];
                count++;
               }
             VolSmoothed[start+i]=sum/count;
            }
         }
    //---
       return(rates_total);
      }
    //+------------------------------------------------------------------+
    

    Şekil 12 hem L1Volatility.mq5 hem de L1VolatilitySmoothed.mq5 göstergelerini göstermektedir.

    Şekil 12. L1Volatility.mq5 ve L1VolatilitySmoothed.mq5 göstergeleri

    Şekil 12. L1Volatility.mq5 ve L1VolatilitySmoothed.mq5 göstergeleri



    3.4.4.3. L1VolatilityAbsolute.mq5 - mutlak volatilite göstergesi

    Gösterge, kapanış fiyatları ile L1 trendi arasındaki farkın mutlak değerini hesaplar.

    Özellikler ve uygulamalar:

    • Hareket yönünü göz ardı eder ve sadece dalgalanma büyüklüğünü değerlendirir;
    • Trend yönünden bağımsız olarak fiyat salınım genliğini analiz etmek için uygundur;
    • Uç değer istatistikleri ve risk analizine dayalı sistemler için kullanışlıdır.

    Mutlak volatilite, fiyat sapmalarının gerçek büyüklüğünü yansıtır ve yatırımcının piyasa hareketinin yönünden etkilenmeden gücünü gözlemlemesini sağlar.

    L1VolatilityAbsolute.mq5 göstergesinin kodu aşağıda verilmiştir.

    //+------------------------------------------------------------------+
    //|                                         L1VolatilityAbsolute.mq5 |
    //|                             Copyright 2000-2026, MetaQuotes Ltd. |
    //|                                             https://www.mql5.com |
    //+------------------------------------------------------------------+
    #property copyright "Copyright 2000-2026, MetaQuotes Ltd."
    #property link      "https://www.mql5.com"
    #property version   "1.00"
    #property indicator_separate_window
    #property indicator_buffers 1
    #property indicator_plots   1
    //---
    #property indicator_label1  "L1VolatilityAbsolute"
    #property indicator_type1   DRAW_LINE
    #property indicator_color1  clrOrange
    #property indicator_width1  2
    //---
    input int    BarsToShow = 1000;  // Number of bars to calculate L1
    input double CoefLambda = 0.015; // Lambda in lambda_max units
    //---
    double Vol[];
    //+------------------------------------------------------------------+
    //| Indicator initialization function                                |
    //+------------------------------------------------------------------+
    int OnInit()
      {
       SetIndexBuffer(0,Vol,INDICATOR_DATA);
       ArrayInitialize(Vol,EMPTY_VALUE);
       PlotIndexSetDouble(0,PLOT_EMPTY_VALUE,EMPTY_VALUE);
       IndicatorSetInteger(INDICATOR_DIGITS,_Digits);
    //---
       return(INIT_SUCCEEDED);
      }
    //+------------------------------------------------------------------+
    //| Indicator iteration function                                     |
    //+------------------------------------------------------------------+
    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[])
      {
    //---
       static bool warned=false;
       if(rates_total < BarsToShow)
         {
          if(!warned)
            {
             Print("Waiting bars ",BarsToShow);
             warned=true;
            }
          ArrayInitialize(Vol,EMPTY_VALUE);
          return(0);
         }
       static datetime last_bar=0;
       bool new_bar=(time[0]!=last_bar);
    //---
       if(!(prev_calculated==0 || new_bar || rates_total!=prev_calculated))
          return(prev_calculated);
    //---
       last_bar=time[0];
       int start=rates_total-BarsToShow;
       int N=BarsToShow;
       for(int i=0; i<start; i++)
          Vol[i]=EMPTY_VALUE;
    //---
       vector<double> price;
       price.Resize(N);
       for(int i=0; i<N; i++)
          price[i]=close[start+i];
       vector<double> l1;
       l1.Resize(N);
       bool res=price.L1TrendFilter(CoefLambda,true,l1);
       if(res)
         {
          for(int i=0; i<N; i++)
             Vol[start+i]=MathAbs(close[start+i]-l1[i]);
         }
    //---
       return(rates_total);
      }
    //+------------------------------------------------------------------+
    

    Göstergenin hesaplanmasının bir örneği Şekil 13'te gösterilmiştir.


    Şekil 13. L1VolatilityAbsolute.mq5 göstergesi

    Şekil 13. L1VolatilityAbsolute.mq5 göstergesi


    3.4.4.4. L1VolatilityNormalized.mq5 - normalleştirilmiş volatilite göstergesi

    Gösterge, L1 trendi ile birlikte ATR (Average True Range) kullanarak volatiliteyi normalleştirir.

    Trendden mutlak fiyat sapmasının ATR periyodu boyunca ortalama fiyat aralığına oranını hesaplar. Normalleştirme, fiyat ölçeği bağımlılığını ortadan kaldırarak farklı enstrümanlar ve zaman dilimleri arasında karşılaştırma yapılmasına olanak tanır.

    Uygulama alanları şunları içerir:

    • Nispeten güçlü ve zayıf piyasa hareketlerinin belirlenmesi;
    • Farklı varlıklar arasındaki volatilitenin karşılaştırılması;
    • Piyasa koşullarının fiyat seviyesinden bağımsız olarak değerlendirilmesi.

    L1VolatilityNormalized.mq5 göstergesinin kodu aşağıda verilmiştir.

    //+------------------------------------------------------------------+
    //|                                       L1VolatilityNormalized.mq5 |
    //|                             Copyright 2000-2026, MetaQuotes Ltd. |
    //|                                             https://www.mql5.com |
    //+------------------------------------------------------------------+
    #property copyright "Copyright 2000-2026, MetaQuotes Ltd."
    #property link      "https://www.mql5.com"
    #property version   "1.00"
    #property indicator_separate_window
    #property indicator_buffers 1
    #property indicator_plots   1
    #property indicator_label1  "L1VolatilityNormalized"
    #property indicator_type1   DRAW_LINE
    #property indicator_color1  clrDodgerBlue
    #property indicator_width1  2
    //---
    input int    BarsToShow = 1000;  // Number of bars to calculate L1
    input double CoefLambda = 0.015; // Lambda in lambda_max units
    //---
    double VolNormalized[];
    //---
    //+------------------------------------------------------------------+
    //| Indicator initialization function                                |
    //+------------------------------------------------------------------+
    int OnInit()
      {
    //--- prepare
       SetIndexBuffer(0, VolNormalized,INDICATOR_DATA);
       ArrayInitialize(VolNormalized,EMPTY_VALUE);
       PlotIndexSetDouble(0,PLOT_EMPTY_VALUE,EMPTY_VALUE);
       IndicatorSetInteger(INDICATOR_DIGITS,_Digits);
    //---
       return(INIT_SUCCEEDED);
      }
    //+------------------------------------------------------------------+
    //| Indicator iteration function                                     |
    //+------------------------------------------------------------------+
    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[])
      {
    //--- check bars
       static bool warned=false;
       if(rates_total<BarsToShow)
         {
          if(!warned)
            {
             Print("Waiting for enough bars: ",BarsToShow);
             warned=true;
            }
          ArrayInitialize(VolNormalized,EMPTY_VALUE);
          return(0);
         }
    //--- check new bar
       static datetime last_bar_time=0;
       bool new_bar=(time[0]!=last_bar_time);
       bool need_recalc=(prev_calculated==0) || new_bar || (rates_total!=prev_calculated);
       if(!need_recalc)
          return(prev_calculated);
       last_bar_time=time[0];
       int start=rates_total-BarsToShow;
    //---
       for(int i=0; i<start; i++)
          VolNormalized[i]=EMPTY_VALUE;
    //--- copy close prices
       vector<double> price(BarsToShow);
       for(int i=0; i<BarsToShow; i++)
          price[i]=close[start+i];
    //---
       vector<double> l1(BarsToShow);
       bool res=price.L1TrendFilter(CoefLambda,true,l1);
       if(res)
         {
          //--- compute normalized volatility
          double mean=0.0;
          double stddev=0.0;
          for(int i=0; i<BarsToShow; i++)
             mean+=close[start+i]-l1[i];
          mean/=BarsToShow;
          //---
          for(int i=0; i<BarsToShow; i++)
             stddev+=MathPow(close[start+i]-l1[i]-mean,2);
          stddev=MathSqrt(stddev/BarsToShow);
          //---
          for(int i=0; i<BarsToShow; i++)
             VolNormalized[start+i]=stddev>0?(close[start+i]-l1[i])/stddev:0;
         }
    //---
       return(rates_total);
      }
    //+------------------------------------------------------------------+
    

    Hesaplama sonucu Şekil 14'de gösterilmiştir.

    Şekil 14. L1VolatilityNormalized.mq5 göstergesi

    Şekil 14. L1VolatilityNormalized.mq5 göstergesi


      3.4.4.5. L1VolatilityNormalizedSmoothed.mq5 - yumuşatılmış normalleştirilmiş volatilite göstergesi

      Bu gösterge, Exponential Moving Average (EMA) yumuşatmasını ekleyerek normalleştirme yaklaşımını genişletir.

      Avantajlar:

      • Kısa süreli gürültü ve keskin sıçramaların etkisini azaltır;
      • Daha net ve daha yorumlanabilir bir volatilite profili oluşturur;
      • Kalıcı volatilitenin ve mevcut piyasa rejiminin değerlendirilmesine yardımcı olur.

      Gösterge özellikle istikrarlı volatilite hesaplaması gerektiren uyarlanabilir stratejiler için, örneğin işlem modlarını otomatik olarak seçerken kullanışlıdır.

      L1VolatilityNormalizedSmoothed.mq5 göstergesinin kodu aşağıda verilmiştir.

      //+------------------------------------------------------------------+
      //|                               L1VolatilityNormalizedSmoothed.mq5 |
      //|                             Copyright 2000-2026, MetaQuotes Ltd. |
      //|                                             https://www.mql5.com |
      //+------------------------------------------------------------------+
      #property copyright "Copyright 2000-2026, MetaQuotes Ltd."
      #property link      "https://www.mql5.com"
      #property version   "1.00"
      #property indicator_separate_window
      #property indicator_buffers 1
      #property indicator_plots   1
      
      #property indicator_label1  "L1VolatilityNormalizedSmoothed"
      #property indicator_type1   DRAW_LINE
      #property indicator_color1  clrDeepSkyBlue
      #property indicator_width1  2
      //---
      input int    BarsToShow   = 1000;  // Number of bars to calculate L1
      input double CoefLambda   = 0.015; // Lambda in lambda_max units
      input int    SmoothPeriod = 10;    // EMA smoothing period (1=no smoothing)
      //---
      double NormVolSmooth[];
      //+------------------------------------------------------------------+
      //| Indicator initialization function                                |
      //+------------------------------------------------------------------+
      int OnInit()
        {
      //--- prepare
         SetIndexBuffer(0,NormVolSmooth,INDICATOR_DATA);
         ArrayInitialize(NormVolSmooth,EMPTY_VALUE);
         PlotIndexSetDouble(0,PLOT_EMPTY_VALUE,EMPTY_VALUE);
         IndicatorSetInteger(INDICATOR_DIGITS,_Digits);
      //---
         return(INIT_SUCCEEDED);
        }
      //+------------------------------------------------------------------+
      //| Indicator iteration function                                     |
      //+------------------------------------------------------------------+
      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[])
        {
      //--- check bars
         static bool warned=false;
         if(rates_total < BarsToShow)
           {
            if(!warned)
              {
               Print("Waiting for enough bars: ",BarsToShow);
               warned=true;
              }
            ArrayInitialize(NormVolSmooth,EMPTY_VALUE);
            return(0);
           }
      //--- check  new bar
         static datetime last_bar_time=0;
         bool new_bar=(time[0]!=last_bar_time);
         bool need_recalc= (prev_calculated==0) || new_bar || (rates_total!=prev_calculated);
         if(!need_recalc)
            return(prev_calculated);
         last_bar_time=time[0];
         int start=rates_total-BarsToShow;
      //---
         for(int i=0; i<start; i++)
            NormVolSmooth[i]=EMPTY_VALUE;
      //--- copy close prices
         vector<double> price(BarsToShow);
         for(int i=0; i<BarsToShow; i++)
            price[i]=close[start+i];
      //---
         vector<double> l1(BarsToShow);
         bool res=price.L1TrendFilter(CoefLambda,true,l1);
         if(res)
           {
            //--- compute normalized volatility
            vector<double> VolNormalized(BarsToShow);
            double mean = 0, stddev = 0;
            for(int i=0; i<BarsToShow; i++)
               mean += close[start+i]-l1[i];
            mean /= BarsToShow;
            //---
            for(int i=0; i<BarsToShow; i++)
               stddev += MathPow(close[start+i]-l1[i]-mean,2);
            stddev = MathSqrt(stddev/BarsToShow);
            //---
            for(int i=0; i<BarsToShow; i++)
               VolNormalized[i]=stddev>0 ? (close[start+i]-l1[i])/stddev: 0;
            //--- EMA smoothing
            vector<double> Smooth(BarsToShow);
            double alpha=(SmoothPeriod<=1) ? 1.0: 2.0/(SmoothPeriod+1.0);
            //---
            Smooth[0] = VolNormalized[0];
            for(int i=1; i<BarsToShow; i++)
               Smooth[i]=alpha*VolNormalized[i]+(1.0-alpha)*Smooth[i-1];
            //--- copy to indicator buffer
            for(int i=0; i<BarsToShow; i++)
               NormVolSmooth[start+i]=Smooth[i];
           }
      //---
         return(rates_total);
        }
      //+------------------------------------------------------------------+
      

      Hesaplama sonucu Şekil 15'te gösterilmiştir.

      Şekil 15. L1VolatilityNormalized.mq5 ve L1VolatilityNormalizedSmoothed.mq5 göstergeleri

      Şekil 15. L1VolatilityNormalized.mq5 ve L1VolatilityNormalizedSmoothed.mq5 göstergeleri



      3.4.4.6. L1VolatilityRegime.mq5 - piyasa rejimi tespiti göstergesi

      Gösterge, normalleştirilmiş ve yumuşatılmış volatiliteye dayalı olarak mevcut piyasa rejimini sınıflandırarak dört piyasa durumunu tanımlar.

      Gösterge özellikleri:

      • Tamamen otonomdur ve harici veri gerektirmez;
      • Uyarlanabilir stratejiler için piyasa dinamiklerinin net bir şekilde görselleştirilmesini sağlar;
      • Eşik parametreleri LowVolThresh ve HighVolThresh farklı enstrümanlar ve zaman dilimleri için ayarlanabilir.
      Değer Rejim Açıklama
      0 Yatay seyir
      Düşük volatilite, yatay piyasa
      1 Trend
      Orta düzeyde volatilite, bir trendin varlığı
      2 Genişleme
      Güçlü hareket, piyasa genişlemesi
      3 Panik Aşırı volatilite, keskin hareketler

      Tablo 3. L1VolatilityRegime.mq5 göstergesinin rejimleri

      • Mevcut piyasa rejimini hızlı bir şekilde belirleyin;
      • Alım-satım stratejilerini mevcut koşullara uyarlayın;
      • Aşırı hareketler sırasında riski azaltın ve alım-satım verimliliğini artırın.

      L1VolatilityRegime.mq5 göstergesinin kodu aşağıda verilmiştir.

      //+------------------------------------------------------------------+
      //|                                           L1VolatilityRegime.mq5 |
      //|                             Copyright 2000-2026, MetaQuotes Ltd. |
      //|                                             https://www.mql5.com |
      //+------------------------------------------------------------------+
      #property copyright "Copyright 2000-2026, MetaQuotes Ltd."
      #property link      "https://www.mql5.com"
      #property version   "1.00"
      #property indicator_separate_window
      #property indicator_buffers 1
      #property indicator_plots   1
      //---
      #property indicator_label1  "L1 Volatility Regime"
      #property indicator_type1   DRAW_LINE
      #property indicator_color1  clrRoyalBlue
      #property indicator_width1  2
      //--- input parameters
      input int    BarsToShow    = 1000;  // Number of bars to calculate L1
      input double CoefLambda    = 0.015; // Lambda in lambda_max units
      input int    ATRPeriod     = 14;    // ATR period
      input int    SmoothPeriod  = 10;    // Smooth period
      input double L1MoveThresh  = 0.0;   // Move volatility
      input double LowVolThresh  = 0.5;   // Low volatility
      input double HighVolThresh = 1.5;   // High volatility
      input double PanicMult     = 2.0;   // Panic volatility
      //---
      double Regime[];
      //+------------------------------------------------------------------+
      //| Indicator initialization function                                |
      //+------------------------------------------------------------------+
      int OnInit()
        {
         SetIndexBuffer(0, Regime, INDICATOR_DATA);
         PlotIndexSetDouble(0,PLOT_EMPTY_VALUE, EMPTY_VALUE);
         IndicatorSetInteger(INDICATOR_DIGITS, 0);
      //---
         return(INIT_SUCCEEDED);
        }
      //+------------------------------------------------------------------+
      //| Indicator iteration function                                     |
      //+------------------------------------------------------------------+
      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[])
        {
      //--- check bars
         if(rates_total<BarsToShow+ATRPeriod)
           {
            ArrayInitialize(Regime,EMPTY_VALUE);
            return(0);
           }
      //--- check new bar
         static datetime last_bar_time=0;
         bool new_bar=(time[0]!=last_bar_time);
         bool need_recalc=(prev_calculated==0) || new_bar || (rates_total!=prev_calculated);
         if(!need_recalc)
            return(prev_calculated);
         last_bar_time=time[0];
      //---
         int start=rates_total-BarsToShow;
         int count=BarsToShow;
      //---
         for(int i=0; i<start; i++)
            Regime[i]=EMPTY_VALUE;
      //---
         vector<double> DataClose(count),DataHigh(count),DataLow(count);
         for(int i=0; i<count; i++)
           {
            DataClose[i]=close[start+i];
            DataHigh[i]=high[start+i];
            DataLow[i]=low[start+i];
           }
      //---
         vector<double> L1(count);
         bool res=DataClose.L1TrendFilter(CoefLambda,true,L1);
         if(!res)
            return(prev_calculated);
      //---
         vector<double> TR(count),ATR(count);
         for(int i=0; i<count; i++)
           {
            if(i==0)
               TR[i]=DataHigh[i]-DataLow[i];
            else
              {
               double h_l=DataHigh[i]-DataLow[i];
               double h_pc=MathAbs(DataHigh[i]-DataClose[i-1]);
               double l_pc=MathAbs(DataLow[i]-DataClose[i-1]);
               TR[i]=MathMax(h_l,MathMax(h_pc,l_pc));
              }
            int from=MathMax(0,i-ATRPeriod+1);
            double sumTR=0.0;
            int n = i-from+1;
            for(int j=from; j<=i;j++)
               sumTR += TR[j];
            ATR[i]=sumTR/n;
           }
      //---
         vector<double> NormVol(count), SmoothVol(count);
         for(int i=0; i<count; i++)
            NormVol[i]=(ATR[i]>0) ? MathAbs(DataClose[i]-L1[i])/ATR[i] : 0;
         double alpha=2.0/(SmoothPeriod+1.0);
         SmoothVol[0]=NormVol[0];
         for(int i=1; i<count; i++)
            SmoothVol[i]=alpha*NormVol[i]+(1.0-alpha)*SmoothVol[i-1];
      //---
         for(int i=0; i<count; i++)
           {
            double vol=SmoothVol[i];
            double deltaL1=(i>0) ? (L1[i]-L1[i-1]): 0.0;
            if(vol<LowVolThresh)
               Regime[start+i]=0; // Range
            else
               if(vol>=LowVolThresh && vol<HighVolThresh)
                  Regime[start+i]=(MathAbs(deltaL1)>L1MoveThresh) ? 1:0; // Trend/Range
               else
                  if(vol>=HighVolThresh && vol<HighVolThresh*PanicMult)
                     Regime[start+i]=2; // Expansion
                  else
                     Regime[start+i]=3; // Panic
           }
      //---
         return(rates_total);
        }
      //+------------------------------------------------------------------+
      

      Hesaplama sonucu Şekil 16'da gösterilmiştir.

      Şekil 16. L1VolatilityRegime.mq5 göstergesi

      Şekil 16. L1VolatilityRegime.mq5 göstergesi

        Kolaylık sağlamak için, renk kodlu rejim görselleştirmesine sahip bir versiyon da kullanılabilir.

        L1VolatilityRegimeColor.mq5 göstergesinin kodu aşağıda verilmiştir.

        //+------------------------------------------------------------------+
        //|                                      L1VolatilityRegimeColor.mq5 |
        //|                             Copyright 2000-2026, MetaQuotes Ltd. |
        //|                                             https://www.mql5.com |
        //+------------------------------------------------------------------+
        #property copyright "Copyright 2000-2026, MetaQuotes Ltd."
        #property link      "https://www.mql5.com"
        #property version   "1.00"
        #property indicator_separate_window
        #property indicator_buffers 4
        #property indicator_plots   4
        //---
        #property indicator_label1  "Range"
        #property indicator_type1   DRAW_LINE
        #property indicator_color1  clrDodgerBlue
        #property indicator_width1  2
        //---
        #property indicator_label2  "Trend"
        #property indicator_type2   DRAW_LINE
        #property indicator_color2  clrLime
        #property indicator_width2  2
        //---
        #property indicator_label3  "Expansion"
        #property indicator_type3   DRAW_LINE
        #property indicator_color3  clrOrange
        #property indicator_width3  2
        //---
        #property indicator_label4  "Panic"
        #property indicator_type4   DRAW_LINE
        #property indicator_color4  clrRed
        #property indicator_width4  2
        //--- input parameters
        input int    BarsToShow    = 1000;  // Number of bars to calculate L1
        input double CoefLambda    = 0.015; // Lambda in lambda_max units
        input int    ATRPeriod     = 14;    // ATR period
        input int    SmoothPeriod  = 10;    // Smooth period
        input double L1MoveThresh  = 0.0;   // Move volatility
        input double LowVolThresh  = 0.5;   // Low volatility
        input double HighVolThresh = 1.5;   // High volatility
        input double PanicMult     = 2.0;   // Panic volatility
        //--- buffers
        double Regime[];
        double BufRange[], BufTrend[], BufExpansion[], BufPanic[];
        //+------------------------------------------------------------------+
        //| Indicator initialization function                                |
        //+------------------------------------------------------------------+
        int OnInit()
          {
           SetIndexBuffer(0,BufRange,INDICATOR_DATA);
           SetIndexBuffer(1,BufTrend,INDICATOR_DATA);
           SetIndexBuffer(2,BufExpansion,INDICATOR_DATA);
           SetIndexBuffer(3,BufPanic,INDICATOR_DATA);
        //---
           PlotIndexSetDouble(0,PLOT_EMPTY_VALUE,EMPTY_VALUE);
           PlotIndexSetDouble(1,PLOT_EMPTY_VALUE,EMPTY_VALUE);
           PlotIndexSetDouble(2,PLOT_EMPTY_VALUE,EMPTY_VALUE);
           PlotIndexSetDouble(3,PLOT_EMPTY_VALUE,EMPTY_VALUE);
        //---
           IndicatorSetInteger(INDICATOR_DIGITS,0);
           return(INIT_SUCCEEDED);
          }
        //+------------------------------------------------------------------+
        //| Indicator iteration function                                     |
        //+------------------------------------------------------------------+
        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[])
          {
           if(rates_total<BarsToShow+ATRPeriod)
             {
              ArrayInitialize(Regime,EMPTY_VALUE);
              ArrayInitialize(BufRange,EMPTY_VALUE);
              ArrayInitialize(BufTrend,EMPTY_VALUE);
              ArrayInitialize(BufExpansion,EMPTY_VALUE);
              ArrayInitialize(BufPanic,EMPTY_VALUE);
              return(0);
             }
        //--- new bars
           static datetime last_bar_time=0;
           bool new_bar=(time[0]!=last_bar_time);
           bool need_recalc=(prev_calculated==0) || new_bar || (rates_total!=prev_calculated);
           if(!need_recalc)
              return(prev_calculated);
           last_bar_time=time[0];
        //---
           int start=rates_total-BarsToShow;
           int count=BarsToShow;
        //---
           ArrayResize(Regime,rates_total);
           for(int i=0;i<start;i++)
              Regime[i]=EMPTY_VALUE;
        //---
           vector<double> DataClose(count),DataHigh(count),DataLow(count);
           for(int i=0; i<count; i++)
             {
              DataClose[i]=close[start+i];
              DataHigh[i]=high[start+i];
              DataLow[i]=low[start+i];
             }
        //---
           vector<double> L1(count);
           bool res=DataClose.L1TrendFilter(CoefLambda,true,L1);
           if(!res)
              return(prev_calculated);
        //---
           vector<double> TR(count),ATR(count);
           for(int i=0; i<count; i++)
             {
              if(i==0)
                 TR[i]=DataHigh[i]-DataLow[i];
              else
                {
                 double h_l = DataHigh[i]-DataLow[i];
                 double h_pc = MathAbs(DataHigh[i]-DataClose[i-1]);
                 double l_pc = MathAbs(DataLow[i]-DataClose[i-1]);
                 TR[i] = MathMax(h_l, MathMax(h_pc, l_pc));
                }
              int from=MathMax(0,i-ATRPeriod+1);
              double sumTR=0;
              int n=i-from+1;
              for(int j=from; j<=i; j++)
                 sumTR+=TR[j];
              ATR[i]=sumTR/n;
             }
        //---
           vector<double> NormVol(count), SmoothVol(count);
           for(int i=0;i<count;i++)
              NormVol[i]=(ATR[i]>0) ? MathAbs(DataClose[i]-L1[i])/ATR[i] : 0;
        //---
           double alpha=2.0/(SmoothPeriod+1.0);
           SmoothVol[0]=NormVol[0];
           for(int i=1; i<count; i++)
              SmoothVol[i]=alpha*NormVol[i]+(1.0-alpha)*SmoothVol[i-1];
        //--- calc Regime[]
           for(int i=0; i<count; i++)
             {
              double vol=SmoothVol[i];
              double deltaL1=(i>0) ? (L1[i]-L1[i-1]):0.0;
              if(vol<LowVolThresh)
                 Regime[start+i]=0;
              else
                 if(vol<HighVolThresh)
                    Regime[start+i]=(MathAbs(deltaL1)>L1MoveThresh) ? 1:0;
                 else
                    if(vol<HighVolThresh*PanicMult)
                       Regime[start+i]=2;
                    else
                       Regime[start+i]=3;
             }
        //--- buffers
           for(int i=0; i<rates_total; i++)
             {
              BufRange[i] = (Regime[i]==0) ? Regime[i]: EMPTY_VALUE;
              BufTrend[i] = (Regime[i]==1) ? Regime[i]: EMPTY_VALUE;
              BufExpansion[i] = (Regime[i]==2) ? Regime[i]: EMPTY_VALUE;
              BufPanic[i] = (Regime[i]==3) ? Regime[i]: EMPTY_VALUE;
             }
        //---
           return(rates_total);
          }
        //+------------------------------------------------------------------+
        

        Birleşik hesaplama sonucu Şekil 17'de gösterilmiştir.

        Şekil 17. L1VolatilityRegime.mq5 ve L1VolatilityRegimeColor.mq5 göstergeleri

        Şekil 17. L1VolatilityRegime.mq5 ve L1VolatilityRegimeColor.mq5 göstergeleri


        Şekil 18-20, EURGBP, AUDCAD ve CHFJPY için tüm volatilite göstergelerinin örneklerini bir arada sunmaktadır.

        Şekil 18. EURGBP için volatilite göstergeleri

        Şekil 18. EURGBP için volatilite göstergeleri


        Şekil 19. AUDCAD için volatilite göstergeleri

        Şekil 19. AUDCAD için volatilite göstergeleri


        Şekil 20. CHFJPY için volatilite göstergeleri

        Şekil 20. CHFJPY için volatilite göstergeleri


        3.5. Alım-Satım Stratejilerinde L1 Trendini Kullanma

        Bu bölümde, işlem sinyali filtreleri uygulamak için farklı seçeneklere sahip Moving Average, MACD, ADX ve EMA alım-satım stratejilerini ele alıyoruz.

        İşlem sinyallerine filtreler eklemek, alım-satım sistemlerinin özelliklerini iyileştirmeye olanak tanır. Sunulan Uzman Danışmanlarda filtre kullanımının etkinliğini analiz etmek için, bakiye ve varlık verilerini (her yeni çubukta) ayrı dosyalara kaydedeceğiz ve farklı modlar altında alım-satım sistemlerinin sonuçlarını görselleştirmek için bir Python komut dosyası kullanacağız.

        Sunulan tüm Uzman Danışmanlar aynı mimariye sahiptir.

        L1 trendini işlem sinyalleriyle uyumlu hale getirmek için genel ilke

        • Açılış filtresi (L1FilterOpen = true), işlemlerin yalnızca baskın trend yönünde açılmasına izin verir.
        • Kapanış filtresi (L1FilterClose = true) güçlü trendler sırasında pozisyonların tutulmasına yardımcı olur ve yerel düzeltmeler sırasında erken çıkışları azaltır.

        Girdi parametreleri (tüm Uzman Danışmanlar için ortaktır):

        //--- L1 filter parameters
        input int    L1TotalBars    = 1000;   // Total bars for L1 filter
        input bool   L1FilterOpen   = false;  // Use filter for Open
        input bool   L1FilterClose  = false;  // Use filter for Close
        input double L1CoefLambda   = 0.2;    // Lambda in lambda_max units
        //--- save statistics
        input bool   SaveStatistics = false;  // Save statistics to file

        Bakiye ve varlık verilerini dosyaya kaydetme

        SaveStatistics girdi parametresi her yeni çubukta zaman, kapanış fiyatı, bakiye, varlık vb. mevcut değerlerin bir dosyaya kaydedilmesini sağlar: terminal_veri_klasörü\Tester\Agent-127.0.0.1-3000\MQL5\Files.

        Verileri kaydetme fonksiyonu OnTick() içinde çağrılır ve bool SaveStatistics girdi parametresinin değerine bağlıdır.

        //+------------------------------------------------------------------+
        //| Expert OnTick function                                           |
        //+------------------------------------------------------------------+
        void OnTick()
          {
        //--- trade only at new bar
           if(!IsNewBar())
              return;
        //--- check trade conditions
           if(SelectPosition())
              CheckForClose();
           else
              CheckForOpen();
        //--- save account statistics
           if(SaveStatistics)
              SaveAccountStatistics();
          }

        Kaydedilen dosya adındaki önek L1FilterOpen ve L1FilterClose kombinasyonuna bağlıdır.

        Dosya adı stratejiye ve sembole bağlıdır ve Uzman Danışmanın başlatma fonksiyonunda oluşturulur:

        //+------------------------------------------------------------------+
        //| PrepareStrategyFileName                                          |
        //+------------------------------------------------------------------+
        string PrepareStrategyFileName(string strategy_name)
          {
           int v=0;
           if(L1FilterOpen)
              v=v | 1;
        //---
           if(L1FilterClose)
              v=v | 2;
        //---
           string filename=IntegerToString(v)+"_"+strategy_name+"_"+_Symbol+".txt";
           return filename;
          }
        //+------------------------------------------------------------------+
        //| Expert initialization                                            |
        //+------------------------------------------------------------------+
        int OnInit()
          {
        //--- prepare filename
           ExtStrategyFileName=PrepareStrategyFileName(ExtStrategyName);
        //--- delete old file if exists
           if(FileIsExist(ExtStrategyFileName))
              FileDelete(ExtStrategyFileName);
        //---
           return INIT_SUCCEEDED;
          }

        Her yeni çubukta istatistikleri bir dosyaya kaydetme fonksiyonu:

        //+------------------------------------------------------------------+
        //| Save account statistics to file                                  |
        //+------------------------------------------------------------------+
        void SaveAccountStatistics()
          {
        //--- check file name
           if(ExtStrategyFileName=="")
              return;
        //---
           int file=FileOpen(ExtStrategyFileName,FILE_WRITE|FILE_READ|FILE_TXT|FILE_SHARE_WRITE|FILE_ANSI);
           if(file==INVALID_HANDLE)
             {
              Print("File open error: ",GetLastError());
              return;
             }
        //--- append
           FileSeek(file,0,SEEK_END);
        //--- account data
           double balance     = AccountInfoDouble(ACCOUNT_BALANCE);
           double equity      = AccountInfoDouble(ACCOUNT_EQUITY);
           double margin      = AccountInfoDouble(ACCOUNT_MARGIN);
           double free_margin = AccountInfoDouble(ACCOUNT_MARGIN_FREE);
           double margin_lvl  = AccountInfoDouble(ACCOUNT_MARGIN_LEVEL);
        //--- volume
           double volume=0.0;
           if(PositionSelect(_Symbol))
              volume=PositionGetDouble(POSITION_VOLUME);
        //--- time
           datetime t[1];
           if(CopyTime(_Symbol,_Period,0,1,t)<=0)
             {
              FileClose(file);
              return;
             }
           double current_close[1];
           if(CopyClose(_Symbol,_Period,0,1,current_close)<=0)
             {
              FileClose(file);
              return;
             }
           string line=StringFormat("%s;%.2f;%.2f;%.2f;%.2f;%.2f;%.2f;%f",TimeToString(t[0],TIME_DATE|TIME_SECONDS),
                                    balance,equity,margin,free_margin,margin_lvl,volume,current_close[0]);
        //---
           FileWrite(file,line);
        //---
           FileClose(file);
          }

        İşlem sinyali filtrelerinin farklı modlarda uygulanması

        Uzman Danışmanın strateji sınayıcıda 4 kombinasyonun tümü ile sıralı yürütülmesi:

        1. L1FilterOpen = false, L1FilterClose = false (filtresiz alım-satım);
        2. L1FilterOpen = true, L1FilterClose = false (giriş filtreleme);
        3. L1FilterOpen = false, L1FilterClose = true (çıkış filtreleme);
        4. L1FilterOpen = true, L1FilterClose = true (giriş ve çıkış filtreleme).

        şu dosyaları oluşturulur: 0_MA_EURUSD.txt, 1_MA_EURUSD.txt, 2_MA_EURUSD.txt, 3_MA_EURUSD.txt.

        Bu dosyalar bakiye/varlık verilerini içerir ve L1 trend uyumunu kullanarak işlem sinyali filtrelemenin etkinliğini karşılaştırmaya olanak tanır.

        Veri görselleştirme

        Birleşik grafikler oluşturmak için 4 dosyayı ayrı bir klasöre kopyalarız ve Python komut dosyasını çalıştırırız (örneğin, "C:\data\").

        import pandas as pd
        import matplotlib.pyplot as plt
        import os
        
        # --- folder for charts
        output_dir = "C:\\data\\charts\\"
        os.makedirs(output_dir, exist_ok=True)
        
        symbol = "EURUSD" 
        name_strategy = "MA"
        file_strategy = name_strategy+"_"+symbol 
        title_strategy = " ("+symbol+" "+name_strategy+" strategy+filters)"
        file_prefix = symbol+"_"+name_strategy+"_"
        
        # --- files
        files = [
            "C:\\data\\0_"+file_strategy+".txt",
            "C:\\data\\1_"+file_strategy+".txt",
            "C:\\data\\2_"+file_strategy+".txt",
            "C:\\data\\3_"+file_strategy+".txt"
        ]
        
        # --- labels
        labels = [
            "No filters",
            "Open L1 filter",
            "Close L1 filter",
            "Open+Close L1 filter"
        ]
        
        # --- load data
        def load_file(filename):
            df = pd.read_csv(
                filename,
                sep=";",
                header=None,
                names=[
                    "time",
                    "balance",
                    "equity",
                    "margin",
                    "free_margin",
                    "margin_level",
                    "volume",
                    "close"
                ]
            )
            df["time"] = pd.to_datetime(df["time"])
            return df
        
        # --- close price chart
        plt.figure(figsize=(10,6), dpi=100)
        for file, label in zip(files, labels):
            df = load_file(file)
        plt.plot(df["time"], df["close"], color='gray')
        plt.title(symbol+" Close Price")
        plt.xlabel("Time")
        plt.ylabel("closing price")
        plt.legend()
        plt.grid()
        plt.tight_layout()
        plt.savefig(output_dir + file_prefix+"close_price.png", dpi=100)
        plt.show()
            
        # --- balance chart
        plt.figure(figsize=(10,6), dpi=100)
        for file, label in zip(files, labels):
            df = load_file(file)
            plt.plot(df["time"], df["balance"], label=label)
        plt.title("Balance" + title_strategy)
        plt.xlabel("Time")
        plt.ylabel("Balance")
        plt.legend()
        plt.grid()
        plt.tight_layout()
        plt.savefig(output_dir + file_prefix+"balance.png", dpi=100)
        plt.show()
        plt.close()
        
        # --- equity chart
        plt.figure(figsize=(10,6), dpi=100)
        for file, label in zip(files, labels):
            df = load_file(file)
            plt.plot(df["time"], df["equity"], label=label)
        plt.title("Equity" + title_strategy)
        plt.xlabel("Time")
        plt.ylabel("Equity")
        plt.legend()
        plt.grid()
        plt.tight_layout()
        plt.savefig(output_dir + file_prefix+"equity.png", dpi=100)
        plt.show()
        plt.close()
        
        #--- balance + equity chart
        plt.figure(figsize=(10,6), dpi=100)
        for i, (file, label) in enumerate(zip(files, labels)):
        
            df = load_file(file)
            # --- get matplotlib color
            color = plt.rcParams["axes.prop_cycle"].by_key()["color"][i % 10]
            #--- equity — solid line
            plt.plot(
                df["time"],
                df["equity"],
                color=color,
                linestyle="-",
                label=f"{label} equity"
            )
            #--- balance — dashed line
            plt.plot(
                df["time"],
                df["balance"],
                color=color,
                linestyle="--",
                label=f"{label} balance"
            )
        plt.title("Balance + Equity" + title_strategy)
        plt.xlabel("Time")
        plt.ylabel("Value")
        plt.legend()
        plt.grid()
        plt.tight_layout()
        plt.savefig(output_dir+file_prefix+"balance_equity.png", dpi=100)
        plt.show()
        plt.close()


        İşlem sinyali filtrelerinin uygulanması

        L1TotalBars, L1FilterOpen, L1FilterClose, L1CoefLambda parametreleri L1 trendi hesaplama ayarlarını ve işlem sinyallerini filtrelemede kullanımını tanımlar.

        L1 trendini hesaplama ve işlem sinyalleriyle uyumlu hale getirme

        Tüm Uzman Danışmanlarda, L1 trendi analizi kullanılarak ek filtreleme uygulanır:

          1. Son L1TotalBars kullanılarak yumuşatılmış bir fiyat serisi oluşturulur;
          2. Trend büyüme katsayısı delta, filtrelenen son iki değer arasındaki fark olarak hesaplanır.
            Delta > 0 ise - yükseliş trendi; delta < 0 ise - düşüş trendi.
            //+------------------------------------------------------------------+
            //| CheckTrendL1                                                     |
            //+------------------------------------------------------------------+
            double CheckTrendL1()
              {
               int max_bars=L1TotalBars;
               MqlRates rates_data[];
               ArrayResize(rates_data,max_bars);
               ArraySetAsSeries(rates_data,false);
               if(CopyRates(_Symbol,_Period,0,max_bars,rates_data) != max_bars)
                 {
                  Print("CopyRates failed for L1Trend");
                  return(0);
                 }
            //--- prepare data (close prices vector)
               int data_count=max_bars;
               vector<double> data_close;
               data_close.Resize(data_count);
               for(int i=0; i<data_count; i++)
                  data_close[i] = rates_data[i].close;
            //--- calculate L1 filter
               vector<double> data_filtered;
               data_filtered.Resize(data_count);
               double dp=0.0;
               bool res=data_close.L1TrendFilter(L1CoefLambda,true,data_filtered);
               if(res)
                  dp = data_filtered[data_count-1]-data_filtered[data_count-2];
            //---
               return(dp);
              }
            

            L1CoefLambda parametresi λmax biriminde belirtilir ve filtrelemeyi volatiliteye ve çubuk sayısına karşı dayanıklı hale getirir.

            İşleme giriş filtresi

            L1FilterOpen = true ise:

            • L1 trendi negatif olduğunda ALIŞ sinyalleri göz ardı edilir;
            • L1 trendi pozitif olduğunda SATIŞ sinyalleri göz ardı edilir.

            İşlemler yalnızca baskın trend yönünde açılır.

            //+------------------------------------------------------------------+
            //| CheckForOpen                                                     |
            //+------------------------------------------------------------------+
            void CheckForOpen()
              {
               ENUM_ORDER_TYPE signal;
               if(!GetTradeSignal(signal))
                  return;
               if(signal == WRONG_VALUE)
                  return;
            //--- L1 filter
               if(L1FilterOpen)
                 {
                  double delta = CheckTrendL1();
                  if(signal == ORDER_TYPE_BUY && delta < 0)
                    {
                     signal = WRONG_VALUE;
                     PrintFormat("Open BUY signal cancelled by L1 trend delta=%.5f", delta);
                    }
                  if(signal == ORDER_TYPE_SELL && delta > 0)
                    {
                     signal = WRONG_VALUE;
                     PrintFormat("Open SELL signal cancelled by L1 trend delta=%.5f", delta);
                    }
                 }
            //---
               if(signal == WRONG_VALUE)
                  return;
            //---
               if(!TerminalInfoInteger(TERMINAL_TRADE_ALLOWED) || Bars(_Symbol,_Period)<L1TotalBars)
                  return;
            //---
               double price = (signal==ORDER_TYPE_BUY) ? SymbolInfoDouble(_Symbol,SYMBOL_ASK): SymbolInfoDouble(_Symbol,SYMBOL_BID);
            //---
               ExtTrade.PositionOpen(_Symbol, signal, TradeLot, price, 0, 0);
              }


            İşlemden çıkış filtresi

            L1FilterClose = true ise:

            • L1 trendi yukarı yönlü olmaya devam ederken ALIŞ pozisyonları ters sinyallerle kapatılmaz;
            • L1 trendi aşağı yönlü olmaya devam ederken SATIŞ pozisyonları kapatılmaz.

            Bu, güçlü trendlerde pozisyonları tutmaya yardımcı olur ve yerel düzeltmeler sırasında erken çıkışları azaltır.

            //+------------------------------------------------------------------+
            //| CheckForClose                                                    |
            //+------------------------------------------------------------------+
            void CheckForClose()
              {
            //--- check position
               if(!PositionSelect(_Symbol))
                  return;
            //--- check position magic
               if(PositionGetInteger(POSITION_MAGIC)!=MA_MAGIC)
                  return;
            //--- check trade signal
               ENUM_ORDER_TYPE signal;
               if(!GetTradeSignal(signal))
                  return;
            //---
               long type = PositionGetInteger(POSITION_TYPE);
               bool close_signal = false;
            //---
               if(type == POSITION_TYPE_BUY && signal == ORDER_TYPE_SELL)
                  close_signal = true;
               if(type == POSITION_TYPE_SELL && signal == ORDER_TYPE_BUY)
                  close_signal = true;
            //--- check L1 filter
               if(L1FilterClose)
                 {
                  double delta = CheckTrendL1();
                  if(type == POSITION_TYPE_BUY && delta > 0)
                    {
                     close_signal = false;
                     PrintFormat("Close BUY signal cancelled by L1 trend delta=%.5f", delta);
                    }
                  if(type == POSITION_TYPE_SELL && delta < 0)
                    {
                     close_signal = false;
                     PrintFormat("Close SELL signal cancelled by L1 trend delta=%.5f", delta);
                    }
                 }
            //---
               if(close_signal && TerminalInfoInteger(TERMINAL_TRADE_ALLOWED) && Bars(_Symbol,_Period)>=L1TotalBars)
                  ExtTrade.PositionClose(_Symbol,3);
              }



            3.5.1. Moving Average Alım-Satım Stratejisi

            İlk örnek olarak, klasik Moving Average trend takip stratejisine dayalı bir Uzman Danışmanı inceleyelim.

            İşlem sinyalleri, kapanış fiyatının hareketli ortalamayı çaprazlamasına dayalı olarak oluşturulur:

            • ALIŞ - kapanış fiyatı hareketli ortalamayı aşağıdan yukarıya doğru çaprazladığında;
            • SATIŞ - kapanış fiyatı hareketli ortalamayı yukarıdan aşağıya doğru çaprazladığında.

            Sinyaller son iki kapanmış çubuk kullanılarak değerlendirilir; bu sayede, mevcut tamamlanmamış çubuğun etkisi ortadan kaldırılır ve yanlış sinyaller azaltılır.

            Ayrıca, L1 trendi ayarlarına göre (L1TotalBars, L1FilterOpen, L1FilterClose, L1CoefLambda) işlem giriş ve çıkış filtreleri uygulanır.

            MovingAverageFilteredL1.mq5 Uzman Danışmanının kodu:

            //+------------------------------------------------------------------+
            //|                                      MovingAverageFilteredL1.mq5 |
            //|                             Copyright 2000-2026, MetaQuotes Ltd. |
            //|                                             https://www.mql5.com |
            //+------------------------------------------------------------------+
            #property copyright "Copyright 2000-2026, MetaQuotes Ltd."
            #property link      "https://www.mql5.com"
            #property version   "1.00"
            //--- best MovingAverage parameters for EURUSD,H1,2025
            input int    MovingPeriod   = 61;     // MA period
            input int    MovingShift    = 0;      // MA shift
            //--- trade volume
            input double TradeLot       = 0.1;    // Lot size
            //--- L1 filter parameters
            input int    L1TotalBars    = 1000;   // Total bars for L1 filter
            input bool   L1FilterOpen   = false;  // Use filter for Open
            input bool   L1FilterClose  = false;  // Use filter for Close
            input double L1CoefLambda   = 0.2;    // Lambda in lambda_max units
            //--- save statistics
            input bool   SaveStatistics = false;  // Save statistics to file
            //---
            #define MA_MAGIC 1234501
            #include <Trade\Trade.mqh>
            CTrade ExtTrade;
            int    ExtHandle = INVALID_HANDLE;
            bool   ExtHedging = false;
            string ExtStrategyName="MA";
            string ExtStrategyFileName="";
            //+------------------------------------------------------------------+
            //| Check new bar                                                    |
            //+------------------------------------------------------------------+
            bool IsNewBar()
              {
               static datetime last_time=0;
               datetime t[1];
            //---
               if(CopyTime(_Symbol,_Period,0,1,t)>0)
                 {
                  if(t[0]!=last_time)
                    {
                     last_time=t[0];
                     return(true);
                    }
                 }
               return(false);
              }
            //+------------------------------------------------------------------+
            //| CheckTrendL1                                                     |
            //+------------------------------------------------------------------+
            double CheckTrendL1()
              {
               int max_bars=L1TotalBars;
               MqlRates rates_data[];
               ArrayResize(rates_data,max_bars);
               ArraySetAsSeries(rates_data,false);
               if(CopyRates(_Symbol,_Period,0,max_bars,rates_data) != max_bars)
                 {
                  Print("CopyRates failed for L1Trend");
                  return(0);
                 }
            //--- prepare data (close prices vector)
               int data_count=max_bars;
               vector<double> data_close;
               data_close.Resize(data_count);
               for(int i=0; i<data_count; i++)
                  data_close[i] = rates_data[i].close;
            //--- calculate L1 filter
               vector<double> data_filtered;
               data_filtered.Resize(data_count);
               double dp=0.0;
               bool res=data_close.L1TrendFilter(L1CoefLambda,true,data_filtered);
               if(res)
                  dp = data_filtered[data_count-1]-data_filtered[data_count-2];
            //---
               return(dp);
              }
            //+------------------------------------------------------------------+
            //| GetTradeSignal                                                   |
            //+------------------------------------------------------------------+
            bool GetTradeSignal(ENUM_ORDER_TYPE &signal)
              {
               signal = WRONG_VALUE;
               MqlRates bars[];
               double ma[];
               ArraySetAsSeries(bars,true);
               ArraySetAsSeries(ma,true);
               ArrayResize(bars,2);
               ArrayResize(ma,2);
            //-- two last closed bars
               if(CopyRates(_Symbol,_Period,2,2,bars) != 2)
                 {
                  Print("CopyRates failed");
                  return(false);
                 }
               if(CopyBuffer(ExtHandle,0,2,2,ma) != 2)
                 {
                  Print("CopyBuffer failed");
                  return(false);
                 }
               double close_prev = bars[1].close;
               double close_last = bars[0].close;
               double ma_prev = ma[1];
               double ma_last = ma[0];
            //--- check MA crossover
               if(close_prev < ma_prev && close_last > ma_last)
                  signal = ORDER_TYPE_BUY;
               else
                  if(close_prev > ma_prev && close_last < ma_last)
                     signal = ORDER_TYPE_SELL;
            //--- log
            //   PrintFormat("PrevBar: time=%s close=%.5f ma=%.5f | LastBar: time=%s close=%.5f ma=%.5f | Signal=%s",
            //               TimeToString(bars[0].time,TIME_DATE|TIME_MINUTES), close_prev, ma_prev,
            //               TimeToString(bars[1].time,TIME_DATE|TIME_MINUTES), close_last, ma_last,
            //               (signal==ORDER_TYPE_BUY?"BUY":signal==ORDER_TYPE_SELL?"SELL":"NONE"));
            //---
               return(true);
              }
            //+------------------------------------------------------------------+
            //| CheckForOpen                                                     |
            //+------------------------------------------------------------------+
            void CheckForOpen()
              {
               ENUM_ORDER_TYPE signal;
               if(!GetTradeSignal(signal))
                  return;
               if(signal == WRONG_VALUE)
                  return;
            //--- L1 filter
               if(L1FilterOpen)
                 {
                  double delta = CheckTrendL1();
                  if(signal == ORDER_TYPE_BUY && delta < 0)
                    {
                     signal = WRONG_VALUE;
                     PrintFormat("Open BUY signal cancelled by L1 trend delta=%.5f", delta);
                    }
                  if(signal == ORDER_TYPE_SELL && delta > 0)
                    {
                     signal = WRONG_VALUE;
                     PrintFormat("Open SELL signal cancelled by L1 trend delta=%.5f", delta);
                    }
                 }
            //---
               if(signal == WRONG_VALUE)
                  return;
            //---
               if(!TerminalInfoInteger(TERMINAL_TRADE_ALLOWED) || Bars(_Symbol,_Period)<L1TotalBars)
                  return;
            //---
               double price = (signal==ORDER_TYPE_BUY) ? SymbolInfoDouble(_Symbol,SYMBOL_ASK): SymbolInfoDouble(_Symbol,SYMBOL_BID);
            //---
               ExtTrade.PositionOpen(_Symbol, signal, TradeLot, price, 0, 0);
              }
            //+------------------------------------------------------------------+
            //| CheckForClose                                                    |
            //+------------------------------------------------------------------+
            void CheckForClose()
              {
            //--- check position
               if(!PositionSelect(_Symbol))
                  return;
            //--- check position magic
               if(PositionGetInteger(POSITION_MAGIC)!=MA_MAGIC)
                  return;
            //--- check trade signal
               ENUM_ORDER_TYPE signal;
               if(!GetTradeSignal(signal))
                  return;
            //---
               long type = PositionGetInteger(POSITION_TYPE);
               bool close_signal = false;
            //---
               if(type == POSITION_TYPE_BUY && signal == ORDER_TYPE_SELL)
                  close_signal = true;
               if(type == POSITION_TYPE_SELL && signal == ORDER_TYPE_BUY)
                  close_signal = true;
            //--- check L1 filter
               if(L1FilterClose)
                 {
                  double delta = CheckTrendL1();
                  if(type == POSITION_TYPE_BUY && delta > 0)
                    {
                     close_signal = false;
                     PrintFormat("Close BUY signal cancelled by L1 trend delta=%.5f", delta);
                    }
                  if(type == POSITION_TYPE_SELL && delta < 0)
                    {
                     close_signal = false;
                     PrintFormat("Close SELL signal cancelled by L1 trend delta=%.5f", delta);
                    }
                 }
            //---
               if(close_signal && TerminalInfoInteger(TERMINAL_TRADE_ALLOWED) && Bars(_Symbol,_Period)>=L1TotalBars)
                  ExtTrade.PositionClose(_Symbol,3);
              }
            //+------------------------------------------------------------------+
            //| SelectPosition                                                   |
            //+------------------------------------------------------------------+
            bool SelectPosition()
              {
               bool res = false;
               if(ExtHedging)
                 {
                  uint total = PositionsTotal();
                  for(uint i=0; i<total; i++)
                    {
                     string sym = PositionGetSymbol(i);
                     if(sym == _Symbol && PositionGetInteger(POSITION_MAGIC)==MA_MAGIC)
                       {
                        res = true;
                        break;
                       }
                    }
                 }
               else
                 {
                  if(PositionSelect(_Symbol))
                     res = (PositionGetInteger(POSITION_MAGIC)==MA_MAGIC);
                 }
               return(res);
              }
            //+------------------------------------------------------------------+
            //| Expert initialization function                                   |
            //+------------------------------------------------------------------+
            int OnInit()
              {
            //--- check parameters
               if(MovingPeriod<=0)
                 {
                  Print("Error: MovingPeriod parameter must be positive");
                  return(INIT_PARAMETERS_INCORRECT);
                 }
               ExtHedging = (AccountInfoInteger(ACCOUNT_MARGIN_MODE)==ACCOUNT_MARGIN_MODE_RETAIL_HEDGING);
               ExtTrade.SetExpertMagicNumber(MA_MAGIC);
               ExtTrade.SetMarginMode();
               ExtTrade.SetTypeFillingBySymbol(_Symbol);
            //--- prepare indicator
               ExtHandle = iMA(_Symbol,_Period,MovingPeriod,MovingShift,MODE_SMA,PRICE_CLOSE);
               if(ExtHandle==INVALID_HANDLE)
                 {
                  Print("Failed to create MA handle");
                  return(INIT_FAILED);
                 }
            //--- prepare filename
               ExtStrategyFileName=PrepareStrategyFileName(ExtStrategyName);
            //--- delete old file if exists
               if(FileIsExist(ExtStrategyFileName))
                  FileDelete(ExtStrategyFileName);
            //---
               return(INIT_SUCCEEDED);
              }
            //+------------------------------------------------------------------+
            //| PrepareStrategyFileName                                          |
            //+------------------------------------------------------------------+
            string PrepareStrategyFileName(string strategy_name)
              {
               int v=0;
               if(L1FilterOpen)
                  v=v | 1;
            //---
               if(L1FilterClose)
                  v=v | 2;
            //---
               string filename=IntegerToString(v)+"_"+strategy_name+"_"+_Symbol+".txt";
               return(filename);
              }
            //+------------------------------------------------------------------+
            //| Save account statistics to file                                  |
            //+------------------------------------------------------------------+
            void SaveAccountStatistics()
              {
            //--- check file name
               if(ExtStrategyFileName=="")
                  return;
            //---
               int file=FileOpen(ExtStrategyFileName,FILE_WRITE|FILE_READ|FILE_TXT|FILE_SHARE_WRITE|FILE_ANSI);
               if(file==INVALID_HANDLE)
                 {
                  Print("File open error: ",GetLastError());
                  return;
                 }
            //--- append
               FileSeek(file,0,SEEK_END);
            //--- account data
               double balance     = AccountInfoDouble(ACCOUNT_BALANCE);
               double equity      = AccountInfoDouble(ACCOUNT_EQUITY);
               double margin      = AccountInfoDouble(ACCOUNT_MARGIN);
               double free_margin = AccountInfoDouble(ACCOUNT_MARGIN_FREE);
               double margin_lvl  = AccountInfoDouble(ACCOUNT_MARGIN_LEVEL);
            //--- volume
               double volume=0.0;
               if(PositionSelect(_Symbol))
                  volume=PositionGetDouble(POSITION_VOLUME);
            //--- time
               datetime t[1];
               if(CopyTime(_Symbol,_Period,0,1,t)<=0)
                 {
                  FileClose(file);
                  return;
                 }
               double current_close[1];
               if(CopyClose(_Symbol,_Period,0,1,current_close)<=0)
                 {
                  FileClose(file);
                  return;
                 }
               string line=StringFormat("%s;%.2f;%.2f;%.2f;%.2f;%.2f;%.2f;%f",TimeToString(t[0],TIME_DATE|TIME_SECONDS),
                                        balance,equity,margin,free_margin,margin_lvl,volume,current_close[0]);
            //---
               FileWrite(file,line);
            //---
               FileClose(file);
              }
            //+------------------------------------------------------------------+
            //| Expert OnTick function                                           |
            //+------------------------------------------------------------------+
            void OnTick()
              {
            //--- trade only at new bar
               if(!IsNewBar())
                  return;
            //--- check trade conditions
               if(SelectPosition())
                  CheckForClose();
               else
                  CheckForOpen();
            //--- save account statistics
               if(SaveStatistics)
                  SaveAccountStatistics();
              }
            //+------------------------------------------------------------------+
            //| Expert deinitialization function                                 |
            //+------------------------------------------------------------------+
            void OnDeinit(const int reason)
              {
            //--- save account statistics
               if(SaveStatistics)
                  SaveAccountStatistics();
            //---
               IndicatorRelease(ExtHandle);
              }
            //+------------------------------------------------------------------+
            

            3.5.1.1. L1 Trend Filtreleme Verimliliğini Değerlendirmek için Genel Metodoloji

            L1 filtresinin etkinliğini değerlendirmek için şunların yapılması gerekir:

            1. Maksimum kar sağlayan alım-satım stratejisinin en iyi parametre setini bulma.
              En iyi performans gösteren stratejilerin işlem sinyallerinin iyileştirilmesi tavsiye edilir.

            2. Filtre kullanımının 4 çeşidi için test sonuçlarını inceleme (L1FilterOpen/L1FilterClose parametrelerini ayarlayarak):
              • Filtresiz alım-satım;
              • Giriş filtreleme ile alım-satım;
              • Çıkış filtreleme ile alım-satım;
              • Hem giriş hem de çıkış filtreleme ile alım-satım.
              Sonuçları bir dosyaya kaydetme (girdi parametresi SaveStatistics=true). Ortaya çıkan dosyalar ayrı bir klasöre kopyalanmalıdır (örneklerde C:\Data\).

            3. Birleşik grafikler oluşturmak için bir Python komut dosyası çalıştırma.

            MovingAverageFilteredL1.mq5 Uzman Danışmanı örneğini kullanarak, EURUSD, H1 zaman dilimi, 2025 yılı test dönemi üzerinde işlem yaparak bu adımları gözden geçirelim.

            Filtre kullanımı için, trend hesaplamasında sabit sayıda çubuk kullanacağız: L1TotalBars = 1000. Düzenlileştirme parametresi λ, L1CoefLambda = 0.2 sabit değeri kullanılarak λmax biriminde belirtilecektir.

            Girdi parametreleri:

            //--- best MovingAverage parameters for EURUSD,H1,2025
            input int    MovingPeriod   = 64;     // MA period
            input int    MovingShift    = 0;      // MA shift
            //--- trade volume
            input double TradeLot       = 0.1;    // Lot size
            //--- L1 filter parameters
            input int    L1TotalBars    = 1000;   // Total bars for L1 filter
            input bool   L1FilterOpen   = false;  // Use filter for Open
            input bool   L1FilterClose  = false;  // Use filter for Close
            input double L1CoefLambda   = 0.2;    // Lambda in lambda_max units
            //--- save statistics
            input bool   SaveStatistics = false;  // Save statistics to file

            Optimizasyon ayarlarında, EURUSD sembolünü, H1 zaman dilimini ve 2025.01.01 ile 2025.12.31 arasındaki test dönemini belirtelim.

            Şekil 21. MovingAverageFilteredL1.mq5 Uzman Danışmanının optimizasyon ayarları

            Şekil 21. MovingAverageFilteredL1.mq5 Uzman Danışmanının optimizasyon ayarları


            Hızlı optimizasyon için “1 dakikalık OHLC” modunu kullanacağız (bu strateji yalnızca yeni çubuklarda çalışır, bu nedenle bu yaklaşım kabul edilebilir) ve “Bakiye maks” ayarıyla “Hızlı genetik tabanlı algoritma” optimizasyon algoritmasını seçeceğiz.

              Şekil 22. MovingAverageFilteredL1.mq5 Uzman Danışmanının optimizasyon parametreleri

            Şekil 22. MovingAverageFilteredL1.mq5 Uzman Danışmanının optimizasyon parametreleri


            Bu aşamadaki amacımız alım-satım stratejisinin en iyi parametrelerini bulmak olduğundan, optimizasyon parametrelerindeki tüm filtreleri ve dosya kaydetmeyi devre dışı bırakıyoruz.

            Basit olması için, “MA Period” adlı tek bir parametreyi 1 ile 800 arasındaki aralıkta, 1'er adımlık artışlarla optimize edeceğiz.

            Şekil 23. MovingAverageFilteredL1.mq5 Uzman Danışmanının optimizasyon sonuçları

            Şekil 23. MovingAverageFilteredL1.mq5 Uzman Danışmanının optimizasyon sonuçları


            “MA Period” parametresinin optimizasyonu 1 dakikadan az sürdü; sonuçlar ve en iyi değerlerin listesi Şekil 23'te gösterilmiştir.

            Daha doğru test için, test ayarlarında “Gerçek tiklere dayanan tüm tikler” seçeneğini seçiyoruz:


            Şekil 24. MovingAverageFilteredL1.mq5 Uzman Danışmanının test ayarları

            Şekil 24. MovingAverageFilteredL1.mq5 Uzman Danışmanının test ayarları


            “MA Period” için en iyi değer olan 64 ile bir tek test çalıştıralım:

            Şekil 25. MovingAverageFilteredL1.mq5 Uzman Danışmanının en iyi optimizasyon parametreleri

            Şekil 25. MovingAverageFilteredL1.mq5 Uzman Danışmanının en iyi optimizasyon parametreleri


            Şekil 26. MovingAverageFilteredL1.mq5 Uzman Danışmanı için en iyi parametrelerle test sonuçları

            Şekil 26. MovingAverageFilteredL1.mq5 Uzman Danışmanı için en iyi parametrelerle test sonuçları


            Şimdi verileri dosyalara kaydederek test çalıştırmamız gerekiyor.

            Bunu yapmak için SaveStatistics = true olarak ayarlayalım ve 4 işlem sinyali filtresi kombinasyonuyla test çalıştıralım. 

            Şekil 27. MovingAverageFilteredL1.mq5 Uzman Danışmanı için sonuçları dosyalara kaydetme parametrelerini test etme

            Şekil 27. MovingAverageFilteredL1.mq5 Uzman Danışmanı için sonuçları dosyalara kaydetme parametrelerini test etme


            Tüm 4 kombinasyonla testleri çalıştırdıktan sonra, sınayıcı dizini şu dosyaları içerecektir: 0_MA_EURUSD.txt, 1_MA_EURUSD.txt, 2_MA_EURUSD.txt ve 3_MA_EURUSD.txt.

            Test aralığının her çubuğu için zaman, kapanış fiyatı, bakiye ve varlık bilgilerini içerirler.

            Ayrı bir dizin oluşturalım ve bunları oraya kopyalayalım (bu örnekte C:\Data).


            Şekil 28. Tüm 4 filtre modu için MovingAverageFilteredL1.mq5 Uzman Danışmanının test sonuçlarını içeren dosyalar

            Şekil 28. Tüm 4 filtre modu için test sonuçlarını içeren dosyalar


            Veri analizi bir Python komut dosyası kullanılarak gerçekleştirilecektir:

            import pandas as pd
            import matplotlib.pyplot as plt
            import os
            
            # --- folder for charts
            output_dir = "C:\\data\\charts\\"
            os.makedirs(output_dir, exist_ok=True)
            
            
            symbol = "EURUSD" 
            name_strategy = "MA"
            file_strategy = name_strategy+"_"+symbol 
            title_strategy = " ("+symbol+" "+name_strategy+" strategy+filters)"
            file_prefix = symbol+"_"+name_strategy+"_"
            
            # --- files
            files = [
                "C:\\data\\0_"+file_strategy+".txt",
                "C:\\data\\1_"+file_strategy+".txt",
                "C:\\data\\2_"+file_strategy+".txt",
                "C:\\data\\3_"+file_strategy+".txt"
            ]
            
            # --- labels
            labels = [
                "No filters",
                "Open L1 filter",
                "Close L1 filter",
                "Open+Close L1 filter"
            ]
            
            # --- load data
            def load_file(filename):
                df = pd.read_csv(
                    filename,
                    sep=";",
                    header=None,
                    names=[
                        "time",
                        "balance",
                        "equity",
                        "margin",
                        "free_margin",
                        "margin_level",
                        "volume",
                        "close"
                    ]
                )
                df["time"] = pd.to_datetime(df["time"])
                return df
            
            # --- close price chart
            plt.figure(figsize=(10,6), dpi=100)
            for file, label in zip(files, labels):
                df = load_file(file)
            plt.plot(df["time"], df["close"], color='gray')
            plt.title(symbol+" Close Price")
            plt.xlabel("Time")
            plt.ylabel("closing price")
            plt.legend()
            plt.grid()
            plt.tight_layout()
            plt.savefig(output_dir + file_prefix+"close_price.png", dpi=100)
            plt.show()
                
            # --- balance chart
            plt.figure(figsize=(10,6), dpi=100)
            for file, label in zip(files, labels):
                df = load_file(file)
                plt.plot(df["time"], df["balance"], label=label)
            plt.title("Balance" + title_strategy)
            plt.xlabel("Time")
            plt.ylabel("Balance")
            plt.legend()
            plt.grid()
            plt.tight_layout()
            plt.savefig(output_dir + file_prefix+"balance.png", dpi=100)
            plt.show()
            plt.close()
            
            # --- equity chart
            plt.figure(figsize=(10,6), dpi=100)
            for file, label in zip(files, labels):
                df = load_file(file)
                plt.plot(df["time"], df["equity"], label=label)
            plt.title("Equity" + title_strategy)
            plt.xlabel("Time")
            plt.ylabel("Equity")
            plt.legend()
            plt.grid()
            plt.tight_layout()
            plt.savefig(output_dir + file_prefix+"equity.png", dpi=100)
            plt.show()
            plt.close()
            
            
            #--- balance + equity chart
            plt.figure(figsize=(10,6), dpi=100)
            for i, (file, label) in enumerate(zip(files, labels)):
            
                df = load_file(file)
                # --- get matplotlib color
                color = plt.rcParams["axes.prop_cycle"].by_key()["color"][i % 10]
                #--- equity — solid line
                plt.plot(
                    df["time"],
                    df["equity"],
                    color=color,
                    linestyle="-",
                    label=f"{label} equity"
                )
                #--- balance — dashed line
                plt.plot(
                    df["time"],
                    df["balance"],
                    color=color,
                    linestyle="--",
                    label=f"{label} balance"
                )
            plt.title("Balance + Equity" + title_strategy)
            plt.xlabel("Time")
            plt.ylabel("Value")
            plt.legend()
            plt.grid()
            plt.tight_layout()
            plt.savefig(output_dir+file_prefix+"balance_equity.png", dpi=100)
            plt.show()
            plt.close()
            
            

            Python komut dosyasını MetaEditor'da çalıştırmak için “Derle” düğmesine basın (Şekil 29).


            Şekil 29. MetaEditor'da PlotData.py komut dosyası

            Şekil 29. MetaEditor'da PlotData.py komut dosyası


            PlotData.py çalıştırıldıktan sonra aşağıdaki grafikler görüntülenecektir:

            1. EURUSD fiyat serisi (her çubuktaki kapanış fiyatları);
            2. Tüm filtre modları için bakiye grafikleri;
            3. Tüm filtre modları için varlık grafikleri;
            4. Birleşik Bakiye + Varlık grafikleri (düşüş azaltımını değerlendirmek için).

            Grafikler ayrıca PNG formatında C:\Data\Charts\ klasörüne kaydedilecektir.


            Şekil 30. Test dönemi için EURUSD fiyat grafiği

            Şekil 30. Test dönemi için EURUSD fiyat grafiği


            Şekil 31. Farklı filtre modlarına göre MovingAverageFilteredL1.mq5 Uzman Danışmanı için bakiye grafikleri

            Şekil 31. Farklı filtre modlarına göre MovingAverageFilteredL1.mq5 Uzman Danışmanı için bakiye grafikleri


            Şekil 32. Farklı filtre modlarına göre MovingAverageFilteredL1.mq5 Uzman Danışmanı için varlık grafikleri

            Şekil 32. Farklı filtre modlarına göre MovingAverageFilteredL1.mq5 Uzman Danışmanı için varlık grafikleri


            Şekil 33. Farklı filtre modlarına göre MovingAverageFilteredL1.mq5 Uzman Danışmanı için birleşik Bakiye ve Varlık grafikleri

            Şekil 33. Farklı filtre modlarına göre MovingAverageFilteredL1.mq5 Uzman Danışmanı için birleşik Bakiye ve Varlık grafikleri


            Python komut dosyalarını MetaEditor'da çalıştırmak için Python'ı yükleyin (örnekte sürüm 3.14) ve ayarlarda yolunu belirtin.


            Şekil 34. MetaEditor'da Python ayarları

            Şekil 34. MetaEditor'da Python ayarları


            Komut dosyası pandas ve matplotlib kütüphanelerini kullanır. Yüklü değillerse:

            pip install pandas
            pip install matplotlib


            3.5.1.2. L1 filtrelerini uygulama sonuçları (Moving Average stratejisi)

            Sonuçlar (bakiye, varlık ve birleşik grafikler) Şekil 35-37'de gösterilmiştir.

            Renkler:

            1. Mavi (filtresiz strateji);
            2. Yeşil (pozisyon kapanışında L1 filtresi);
            3. Kırmızı (hem açılış hem de kapanışta L1 filtresi);
            4. Turuncu (açılışta L1 filtresi).

            Şekil 35. Farklı filtre modlarına göre MovingAverageFilteredL1.mq5 Uzman Danışmanı için bakiye grafikleri

            Şekil 35. Farklı filtre modlarına göre MovingAverageFilteredL1.mq5 Uzman Danışmanı için bakiye grafikleri



            Şekil 36. Farklı filtre modlarına göre MovingAverageFilteredL1.mq5 Uzman Danışmanı için varlık grafikleri

            Şekil 36. Farklı filtre modlarına göre MovingAverageFilteredL1.mq5 Uzman Danışmanı için varlık grafikleri


            Şekil 37. Farklı filtre modlarına göre MovingAverageFilteredL1.mq5 Uzman Danışmanı için birleşik Bakiye ve Varlık grafikleri

            Şekil 37. Farklı filtre modlarına göre MovingAverageFilteredL1.mq5 Uzman Danışmanı için birleşik Bakiye ve Varlık grafikleri



            3.5.2. MACD Alım-Satım Stratejisi

            Başka bir örnek olarak, MACD (Moving Average Convergence/Divergence) göstergesi üzerine kurulu bir alım-satım stratejisinin sinyallerine göre işlem yapan bir Uzman Danışmanı inceleyelim.

            İşlem sinyalleri

            MACD ana çizgisi sinyal çizgisini çaprazladığında sinyaller üretilir:

            1. ALIŞ - MACD ana çizgisi sinyal çizgisini aşağıdan yukarıya doğru çaprazladığında.
            2. SATIŞ - MACD ana çizgisi sinyal çizgisini yukarıdan aşağıya doğru çaprazladığında.

            Sinyaller yalnızca çubuk kapanışında analiz edilir, bu da çubuk içindeki yanlış tetiklemelerin sayısını azaltır.

            Ayrıca, L1 trendi ayarlarına göre (L1TotalBars, L1FilterOpen, L1FilterClose, L1CoefLambda) işlem giriş ve çıkış filtreleri kullanılır.

            MACDFilteredL1.mq5 Uzman Danışmanının kodu:

            //+------------------------------------------------------------------+
            //|                                               MACDFilteredL1.mq5 |
            //|                             Copyright 2000-2026, MetaQuotes Ltd. |
            //|                                             https://www.mql5.com |
            //+------------------------------------------------------------------+
            #property copyright "Copyright 2000-2026, MetaQuotes Ltd."
            #property link      "https://www.mql5.com"
            #property version   "1.00"
            //--- best MACD parameters for USDCHF,H1,2025
            input int    FastEMA        = 43;     // Fast EMA
            input int    SlowEMA        = 59;     // Slow EMA
            input int    SignalEMA      = 37;     // SignalEMA
            //--- trade volume
            input double TradeLot       = 0.1;    // Lot size
            //--- L1 filter parameters
            input int    L1TotalBars    = 1000;   // Total bars for L1 filter
            input bool   L1FilterOpen   = false;  // Use filter for Open
            input bool   L1FilterClose  = false;  // Use filter for Close
            input double L1CoefLambda   = 0.2;    // Lambda in lambda_max units
            //--- save statistics
            input bool   SaveStatistics = false;  // Save statistics to file
            //---
            #define MACD_MAGIC 1234502
            #include <Trade\Trade.mqh>
            int    ExtHandle = INVALID_HANDLE;
            bool   ExtHedging = false;
            CTrade ExtTrade;
            string ExtStrategyName="MACD";
            string ExtStrategyFileName="";
            //+------------------------------------------------------------------+
            //| Check new bar                                                    |
            //+------------------------------------------------------------------+
            bool IsNewBar()
              {
               static datetime last_time=0;
               datetime t[1];
            //---
               if(CopyTime(_Symbol,_Period,0,1,t)>0)
                 {
                  if(t[0]!=last_time)
                    {
                     last_time=t[0];
                     return(true);
                    }
                 }
               return(false);
              }
            //+------------------------------------------------------------------+
            //| CheckTrendL1                                                     |
            //+------------------------------------------------------------------+
            double CheckTrendL1()
              {
               int max_bars=L1TotalBars;
               MqlRates rates_data[];
               ArrayResize(rates_data,max_bars);
               ArraySetAsSeries(rates_data,false);
               if(CopyRates(_Symbol,_Period,0,max_bars,rates_data) != max_bars)
                 {
                  Print("CopyRates failed for L1Trend");
                  return(0);
                 }
            //--- prepare data (close prices vector)
               int data_count=max_bars;
               vector<double> data_close;
               data_close.Resize(data_count);
               for(int i=0; i<data_count; i++)
                  data_close[i] = rates_data[i].close;
            //--- calculate L1 filter
               vector<double> data_filtered;
               data_filtered.Resize(data_count);
               double dp=0.0;
               bool res=data_close.L1TrendFilter(L1CoefLambda,true,data_filtered);
               if(res)
                  dp = data_filtered[data_count-1]-data_filtered[data_count-2];
            //---
               return(dp);
              }
            //+------------------------------------------------------------------+
            //| GetTradeSignal(MACD)                                             |
            //+------------------------------------------------------------------+
            bool GetTradeSignal(ENUM_ORDER_TYPE &signal)
              {
               signal = WRONG_VALUE;
               double macd_main[];
               double macd_signal[];
            //---
               ArrayResize(macd_main,2);
               ArrayResize(macd_signal,2);
            //---
               ArraySetAsSeries(macd_main, true);
               ArraySetAsSeries(macd_signal, true);
            //--- buffer 0 = MACD main, buffer 1 = signal line
               if(CopyBuffer(ExtHandle,0,1,2,macd_main)!=2)
                  return(false);
               if(CopyBuffer(ExtHandle,1,1,2,macd_signal)!=2)
                  return(false);
            //---
               double main_prev   = macd_main[1];
               double main_last   = macd_main[0];
               double signal_prev = macd_signal[1];
               double signal_last = macd_signal[0];
            //--- MACD crossover
               if(main_prev < signal_prev && main_last > signal_last)
                  signal = ORDER_TYPE_BUY;
               else
                  if(main_prev > signal_prev && main_last < signal_last)
                     signal = ORDER_TYPE_SELL;
            //---
               return(true);
              }
            //+------------------------------------------------------------------+
            //| CheckForOpen                                                     |
            //+------------------------------------------------------------------+
            void CheckForOpen()
              {
               ENUM_ORDER_TYPE signal;
               if(!GetTradeSignal(signal) || signal == WRONG_VALUE)
                  return;
            //---
               if(L1FilterOpen)
                 {
                  double dp = CheckTrendL1();
                  if(signal == ORDER_TYPE_BUY && dp < 0)
                     return;
                  if(signal == ORDER_TYPE_SELL && dp > 0)
                     return;
                 }
            //---
               if(!TerminalInfoInteger(TERMINAL_TRADE_ALLOWED) || Bars(_Symbol, _Period) < L1TotalBars)
                  return;
            //---
               double price = (signal == ORDER_TYPE_BUY)
                              ? SymbolInfoDouble(_Symbol, SYMBOL_ASK)
                              : SymbolInfoDouble(_Symbol, SYMBOL_BID);
            //---
               ExtTrade.PositionOpen(_Symbol, signal, TradeLot, price, 0, 0);
              }
            //+------------------------------------------------------------------+
            //| CheckForClose                                                    |
            //+------------------------------------------------------------------+
            void CheckForClose()
              {
            //--- check position
               if(!PositionSelect(_Symbol))
                  return;
            //--- check position magic
               if(PositionGetInteger(POSITION_MAGIC)!=MACD_MAGIC)
                  return;
            //--- check trade signal
               ENUM_ORDER_TYPE signal;
               if(!GetTradeSignal(signal))
                  return;
            //---
               long type = PositionGetInteger(POSITION_TYPE);
               bool close_signal = false;
            //---
               if(type == POSITION_TYPE_BUY  && signal == ORDER_TYPE_SELL)
                  close_signal = true;
               if(type == POSITION_TYPE_SELL && signal == ORDER_TYPE_BUY)
                  close_signal = true;
            //--- check L1 filter
               if(L1FilterClose)
                 {
                  double dp = CheckTrendL1();
                  if(type == POSITION_TYPE_BUY && dp > 0)
                    {
                     close_signal = false;
                     PrintFormat("Close BUY signal cancelled by L1 trend dp=%.5f", dp);
                    }
                  if(type == POSITION_TYPE_SELL && dp < 0)
                    {
                     close_signal = false;
                     PrintFormat("Close SELL signal cancelled by L1 trend dp=%.5f", dp);
                    }
                 }
            //---
               if(close_signal)
                  ExtTrade.PositionClose(_Symbol, 3);
              }
            //+------------------------------------------------------------------+
            //| SelectPosition                                                   |
            //+------------------------------------------------------------------+
            bool SelectPosition()
              {
               bool res = false;
               if(ExtHedging)
                 {
                  uint total = PositionsTotal();
                  for(uint i=0; i<total; i++)
                    {
                     string sym = PositionGetSymbol(i);
                     if(sym == _Symbol && PositionGetInteger(POSITION_MAGIC)==MACD_MAGIC)
                       {
                        res = true;
                        break;
                       }
                    }
                 }
               else
                 {
                  if(PositionSelect(_Symbol))
                     res = (PositionGetInteger(POSITION_MAGIC)==MACD_MAGIC);
                 }
               return(res);
              }
            //+------------------------------------------------------------------+
            //| Expert initialization                                            |
            //+------------------------------------------------------------------+
            int OnInit()
              {
            //--- check parameters
               if(FastEMA <= 0 || SlowEMA <= 0 || SignalEMA <= 0)
                 {
                  Print("Error: MACD parameters must be positive");
                  return(INIT_PARAMETERS_INCORRECT);
                 }
               if(FastEMA >= SlowEMA)
                 {
                  Print("FastEMA must be less than SlowEMA");
                  return(INIT_PARAMETERS_INCORRECT);
                 }
            //---
               ExtHedging = (AccountInfoInteger(ACCOUNT_MARGIN_MODE)==ACCOUNT_MARGIN_MODE_RETAIL_HEDGING);
               ExtTrade.SetExpertMagicNumber(MACD_MAGIC);
               ExtTrade.SetMarginMode();
               ExtTrade.SetTypeFillingBySymbol(_Symbol);
            //--- prepare indicator
               ExtHandle=iMACD(_Symbol,_Period,FastEMA,SlowEMA,SignalEMA,PRICE_CLOSE);
               if(ExtHandle==INVALID_HANDLE)
                 {
                  Print("Failed to create MACD handle");
                  return(INIT_FAILED);
                 }
            //--- prepare filename
               ExtStrategyFileName=PrepareStrategyFileName(ExtStrategyName);
            //--- delete old file if exists
               if(FileIsExist(ExtStrategyFileName))
                  FileDelete(ExtStrategyFileName);
            //---
               return(INIT_SUCCEEDED);
              }
            //+------------------------------------------------------------------+
            //| PrepareStrategyFileName                                          |
            //+------------------------------------------------------------------+
            string PrepareStrategyFileName(string strategy_name)
              {
               int v=0;
               if(L1FilterOpen)
                  v=v | 1;
            //---
               if(L1FilterClose)
                  v=v | 2;
            //---
               string filename=IntegerToString(v)+"_"+strategy_name+"_"+_Symbol+".txt";
               return(filename);
              }
            //+------------------------------------------------------------------+
            //| Save account statistics to file                                  |
            //+------------------------------------------------------------------+
            void SaveAccountStatistics()
              {
            //--- check file name
               if(ExtStrategyFileName=="")
                  return;
            //---
               int file=FileOpen(ExtStrategyFileName,FILE_WRITE|FILE_READ|FILE_TXT|FILE_SHARE_WRITE|FILE_ANSI);
               if(file==INVALID_HANDLE)
                 {
                  Print("File open error: ",GetLastError());
                  return;
                 }
            //--- append
               FileSeek(file,0,SEEK_END);
            //--- account data
               double balance     = AccountInfoDouble(ACCOUNT_BALANCE);
               double equity      = AccountInfoDouble(ACCOUNT_EQUITY);
               double margin      = AccountInfoDouble(ACCOUNT_MARGIN);
               double free_margin = AccountInfoDouble(ACCOUNT_MARGIN_FREE);
               double margin_lvl  = AccountInfoDouble(ACCOUNT_MARGIN_LEVEL);
            //--- volume
               double volume=0.0;
               if(PositionSelect(_Symbol))
                  volume=PositionGetDouble(POSITION_VOLUME);
            //--- time
               datetime t[1];
               if(CopyTime(_Symbol,_Period,0,1,t)<=0)
                 {
                  FileClose(file);
                  return;
                 }
               double current_close[1];
               if(CopyClose(_Symbol,_Period,0,1,current_close)<=0)
                 {
                  FileClose(file);
                  return;
                 }
               string line=StringFormat("%s;%.2f;%.2f;%.2f;%.2f;%.2f;%.2f;%f",TimeToString(t[0],TIME_DATE|TIME_SECONDS),
                                        balance,equity,margin,free_margin,margin_lvl,volume,current_close[0]);
            //---
               FileWrite(file,line);
            //---
               FileClose(file);
              }
            //+------------------------------------------------------------------+
            //| Expert OnTick function                                           |
            //+------------------------------------------------------------------+
            void OnTick()
              {
            //--- trade only at new bar
               if(!IsNewBar())
                  return;
            //--- check trade conditions
               if(SelectPosition())
                  CheckForClose();
               else
                  CheckForOpen();
            //--- save account statistics
               if(SaveStatistics)
                  SaveAccountStatistics();
              }
            //+------------------------------------------------------------------+
            //| Expert deinitialization                                          |
            //+------------------------------------------------------------------+
            void OnDeinit(const int reason)
              {
            //--- save account statistics
               if(SaveStatistics)
                  SaveAccountStatistics();
            //---
               if(ExtHandle != INVALID_HANDLE)
                  IndicatorRelease(ExtHandle);
              }
            //+------------------------------------------------------------------+
            

            Test ayarları Şekil 38'de gösterilmiştir.

            Şekil 38. MACDFilteredL1.mq5 Uzman Danışmanının Strateji Sınayıcı ayarları

            Şekil 38. MACDFilteredL1.mq5 Uzman Danışmanının Strateji Sınayıcı ayarları


            Şekil 39. MACDFilteredL1.mq5 Uzman Danışmanının test parametreleri

            Şekil 39. MACDFilteredL1.mq5 Uzman Danışmanının test parametreleri


            Şekil 40. MACDFilteredL1.mq5 Uzman Danışmanının test sonuçları

            Şekil 40. MACDFilteredL1.mq5 Uzman Danışmanının test sonuçları


            Şekil 41. MACDFilteredL1.mq5 Uzman Danışmanı için sonuçları dosyalara kaydetme parametrelerini test etme

            Şekil 41. MACDFilteredL1.mq5 Uzman Danışmanı için sonuçları dosyalara kaydetme parametrelerini test etme


            Strateji sınayıcıda farklı filtre yapılandırmalarıyla testler çalıştırdıktan sonra, x_MACD_EURUSD.txt dosyaları sınayıcı dizininde görünecektir.

            Bunlar C:\Data\ klasörüne kopyalanmalı ve PlotData.py komut dosyası çalıştırılmalıdır.


            Şekil 42. Farklı filtre modları için MACDFilteredL1.mq5 Uzman Danışmanının test sonuçlarını içeren dosyalar

            Şekil 42. Farklı filtre modları için MACDFilteredL1.mq5 Uzman Danışmanının test sonuçlarını içeren dosyalar



            3.5.2.1. L1 filtrelerini uygulama sonuçları (MACD stratejisi)

            Sonuçlar Şekil 43-45'te gösterilmiştir.

            Şekil 43. Farklı filtre modlarına göre MACDFilteredL1.mq5 Uzman Danışmanı için bakiye grafikleri

            Şekil 43. Farklı filtre modlarına göre MACDFilteredL1.mq5 Uzman Danışmanı için bakiye grafikleri


            Şekil 44. Farklı filtre modlarına göre MACDFilteredL1.mq5 Uzman Danışmanı için varlık grafikleri

            Şekil 44. Farklı filtre modlarına göre MACDFilteredL1.mq5 Uzman Danışmanı için varlık grafikleri


            Şekil 45. Farklı filtre modlarına göre MACDFilteredL1.mq5 Uzman Danışmanı için birleşik Bakiye ve Varlık grafikleri

            Şekil 45. Farklı filtre modlarına göre MACDFilteredL1.mq5 Uzman Danışmanı için birleşik Bakiye ve Varlık grafikleri


            3.5.3. ADX Alım-Satım Stratejisi

            Başka bir örnek olarak, Average Directional Movement Index (ADX) göstergesine dayalı bir trend takip stratejisi uygulayan ADXFilteredL1.mq5 Uzman Danışmanını ele alalım.

            Ana işlem sinyalleri, +DI ve -DI çizgilerinin ve trend gücünü karakterize eden ADX seviyesinin analizine dayanmaktadır.

            Sinyaller aşağıdaki gibi tanımlanır:

            • ALIŞ: +DI, -DI'yı aşağıdan yukarıya doğru çaprazladığında;
            • SATIŞ: +DI, -DI'yı yukarıdan aşağıya doğru çaprazladığında.

            Ek olarak, ADX değeri de dikkate alınır. ADX, ADXTrendLevel eşiğinin altındaysa, piyasa zayıf trendli veya yatay seyirli olarak değerlendirilir ve sinyal göz ardı edilir.

            Son iki kapanmış çubuktaki gösterge değerleri kullanılır, mevcut tamamlanmamış çubuğun etkisi hariç tutulur ve yanlış sinyaller azaltılır.

            Ayrıca, L1 trendi ayarlarına göre (L1TotalBars, L1FilterOpen, L1FilterClose, L1CoefLambda) işlem giriş ve çıkış filtreleri uygulanır.

            ADXFilteredL1.mq5 Uzman Danışmanının kodu aşağıda sunulmuştur.

            //+------------------------------------------------------------------+
            //|                                                ADXFilteredL1.mq5 |
            //|                             Copyright 2000-2026, MetaQuotes Ltd. |
            //|                                              http://www.mql5.com |
            //+------------------------------------------------------------------+
            #property copyright "Copyright 2000-2026, MetaQuotes Ltd."
            #property link      "https://www.mql5.com"
            #property version   "1.00"
            //--- best ADX parameters for EURUSD,H1,2025
            input int    ADXPeriod      = 65;     // ADX Period
            input double ADXTrendLevel  = 7;      // ADX Trend Level
            //--- trade volume
            input double TradeLot       = 0.1;    // Lot size
            //--- L1 filter parameters
            input int    L1TotalBars    = 1000;   // Total bars for L1 filter
            input bool   L1FilterOpen   = false;  // Use filter for Open
            input bool   L1FilterClose  = false;  // Use filter for Close
            input double L1CoefLambda   = 0.2;    // Lambda in lambda_max units
            //--- save statistics
            input bool   SaveStatistics = false;  // Save statistics to file
            //---
            #define ADX_MAGIC 1234503
            #include <Trade\Trade.mqh>
            CTrade ExtTrade;
            int    ExtHandle = INVALID_HANDLE;
            bool   ExtHedging = false;
            string ExtStrategyName="ADX";
            string ExtStrategyFileName="";
            //+------------------------------------------------------------------+
            //| Check new bar                                                    |
            //+------------------------------------------------------------------+
            bool IsNewBar()
              {
               static datetime last_time=0;
               datetime t[1];
            //---
               if(CopyTime(_Symbol,_Period,0,1,t)>0)
                 {
                  if(t[0]!=last_time)
                    {
                     last_time=t[0];
                     return(true);
                    }
                 }
               return(false);
              }
            //+------------------------------------------------------------------+
            //| CheckTrendL1                                                     |
            //+------------------------------------------------------------------+
            double CheckTrendL1()
              {
               int max_bars=L1TotalBars;
               MqlRates rates_data[];
               ArrayResize(rates_data,max_bars);
               ArraySetAsSeries(rates_data,false);
               if(CopyRates(_Symbol,_Period,0,max_bars,rates_data) != max_bars)
                 {
                  Print("CopyRates failed for L1Trend");
                  return(0);
                 }
            //--- prepare data (close prices vector)
               int data_count=max_bars;
               vector<double> data_close;
               data_close.Resize(data_count);
               for(int i=0; i<data_count; i++)
                  data_close[i] = rates_data[i].close;
            //--- calculate L1 filter
               vector<double> data_filtered;
               data_filtered.Resize(data_count);
               double dp=0.0;
               bool res=data_close.L1TrendFilter(L1CoefLambda,true,data_filtered);
               if(res)
                  dp = data_filtered[data_count-1] - data_filtered[data_count-2];
            //---
               return(dp);
              }
            //+------------------------------------------------------------------+
            //| GetTradeSignal (ADX)                                             |
            //+------------------------------------------------------------------+
            bool GetTradeSignal(ENUM_ORDER_TYPE &signal)
              {
               signal=WRONG_VALUE;
               double adx[],plusdi[],minusdi[];
               ArrayResize(adx,2);
               ArrayResize(plusdi,2);
               ArrayResize(minusdi,2);
            //---
               ArraySetAsSeries(adx,true);
               ArraySetAsSeries(plusdi,true);
               ArraySetAsSeries(minusdi,true);
            //--- buffer0 = ADX
               if(CopyBuffer(ExtHandle,0,1,2,adx)!=2)
                  return(false);
            //--- buffer1 = +DI
               if(CopyBuffer(ExtHandle,1,1,2,plusdi)!=2)
                  return(false);
            //--- buffer2 = -DI
               if(CopyBuffer(ExtHandle,2,1,2,minusdi)!=2)
                  return(false);
               double adx_last=adx[0];
               double plus_prev=plusdi[1];
               double plus_last=plusdi[0];
               double minus_prev=minusdi[1];
               double minus_last=minusdi[0];
            //--- strong trend required
               if(adx_last<ADXTrendLevel)
                  return(true);
            //--- +DI cross -DI
               if(plus_prev<minus_prev && plus_last>minus_last)
                  signal=ORDER_TYPE_BUY;
               else
                  if(plus_prev>minus_prev && plus_last<minus_last)
                     signal=ORDER_TYPE_SELL;
            //---
               return(true);
              }
            //+------------------------------------------------------------------+
            //| CheckForOpen                                                     |
            //+------------------------------------------------------------------+
            void CheckForOpen()
              {
               ENUM_ORDER_TYPE signal;
            //---
               if(!GetTradeSignal(signal) || signal==WRONG_VALUE)
                  return;
            //---
               if(L1FilterOpen)
                 {
                  double dp=CheckTrendL1();
                  if(signal==ORDER_TYPE_BUY && dp<0)
                     return;
                  if(signal==ORDER_TYPE_SELL && dp>0)
                     return;
                 }
            //---
               if(!TerminalInfoInteger(TERMINAL_TRADE_ALLOWED) || Bars(_Symbol,_Period)<L1TotalBars)
                  return;
            //---
               double price=(signal==ORDER_TYPE_BUY)
                            ? SymbolInfoDouble(_Symbol,SYMBOL_ASK)
                            : SymbolInfoDouble(_Symbol,SYMBOL_BID);
            //---
               ExtTrade.PositionOpen(_Symbol,signal,TradeLot,price,0,0);
              }
            //+------------------------------------------------------------------+
            //| CheckForClose                                                    |
            //+------------------------------------------------------------------+
            void CheckForClose()
              {
            //--- check position
               if(!PositionSelect(_Symbol))
                  return;
            //--- check position magic
               if(PositionGetInteger(POSITION_MAGIC)!=ADX_MAGIC)
                  return;
            //--- check trade signal
               ENUM_ORDER_TYPE signal;
               if(!GetTradeSignal(signal))
                  return;
            //---
               long type=PositionGetInteger(POSITION_TYPE);
               bool close_signal=false;
            //---
               if(type==POSITION_TYPE_BUY && signal==ORDER_TYPE_SELL)
                  close_signal=true;
            //---
               if(type==POSITION_TYPE_SELL && signal==ORDER_TYPE_BUY)
                  close_signal=true;
            //--- check L1 filter
            //--- check L1 filter
               if(L1FilterClose)
                 {
                  double dp = CheckTrendL1();
                  if(type == POSITION_TYPE_BUY && dp > 0)
                    {
                     close_signal = false;
                     PrintFormat("Close BUY signal cancelled by L1 trend dp=%.5f", dp);
                    }
                  if(type == POSITION_TYPE_SELL && dp < 0)
                    {
                     close_signal = false;
                     PrintFormat("Close SELL signal cancelled by L1 trend dp=%.5f", dp);
                    }
                 }
            //---
               if(close_signal)
                  ExtTrade.PositionClose(_Symbol,3);
              }
            //+------------------------------------------------------------------+
            //| SelectPosition                                                   |
            //+------------------------------------------------------------------+
            bool SelectPosition()
              {
               bool res = false;
               if(ExtHedging)
                 {
                  uint total = PositionsTotal();
                  for(uint i=0; i<total; i++)
                    {
                     string sym = PositionGetSymbol(i);
                     if(sym == _Symbol && PositionGetInteger(POSITION_MAGIC)==ADX_MAGIC)
                       {
                        res = true;
                        break;
                       }
                    }
                 }
               else
                 {
                  if(PositionSelect(_Symbol))
                     res = (PositionGetInteger(POSITION_MAGIC)==ADX_MAGIC);
                 }
               return(res);
              }
            //+------------------------------------------------------------------+
            //| Expert initialization                                            |
            //+------------------------------------------------------------------+
            int OnInit()
              {
               ExtHedging = (AccountInfoInteger(ACCOUNT_MARGIN_MODE)==ACCOUNT_MARGIN_MODE_RETAIL_HEDGING);
               ExtTrade.SetExpertMagicNumber(ADX_MAGIC);
               ExtTrade.SetMarginMode();
               ExtTrade.SetTypeFillingBySymbol(_Symbol);
            //--- prepare indicator
               ExtHandle=iADX(_Symbol,_Period,ADXPeriod);
               if(ExtHandle==INVALID_HANDLE)
                 {
                  Print("ADX handle error");
                  return(INIT_FAILED);
                 }
            //--- prepare filename
               ExtStrategyFileName=PrepareStrategyFileName(ExtStrategyName);
            //--- delete old file if exists
               if(FileIsExist(ExtStrategyFileName))
                  FileDelete(ExtStrategyFileName);
            //---
               return(INIT_SUCCEEDED);
              }
            //+------------------------------------------------------------------+
            //| PrepareStrategyFileName                                          |
            //+------------------------------------------------------------------+
            string PrepareStrategyFileName(string strategy_name)
              {
               int v=0;
               if(L1FilterOpen)
                  v=v | 1;
            //---
               if(L1FilterClose)
                  v=v | 2;
            //---
               string filename=IntegerToString(v)+"_"+strategy_name+"_"+_Symbol+".txt";
               return(filename);
              }
            //+------------------------------------------------------------------+
            //| Save account statistics to file                                  |
            //+------------------------------------------------------------------+
            void SaveAccountStatistics()
              {
            //--- check file name
               if(ExtStrategyFileName=="")
                  return;
            //---
               int file=FileOpen(ExtStrategyFileName,FILE_WRITE|FILE_READ|FILE_TXT|FILE_SHARE_WRITE|FILE_ANSI);
               if(file==INVALID_HANDLE)
                 {
                  Print("File open error: ",GetLastError());
                  return;
                 }
            //--- append
               FileSeek(file,0,SEEK_END);
            //--- account data
               double balance     = AccountInfoDouble(ACCOUNT_BALANCE);
               double equity      = AccountInfoDouble(ACCOUNT_EQUITY);
               double margin      = AccountInfoDouble(ACCOUNT_MARGIN);
               double free_margin = AccountInfoDouble(ACCOUNT_MARGIN_FREE);
               double margin_lvl  = AccountInfoDouble(ACCOUNT_MARGIN_LEVEL);
            //--- volume
               double volume=0.0;
               if(PositionSelect(_Symbol))
                  volume=PositionGetDouble(POSITION_VOLUME);
            //--- time
               datetime t[1];
               if(CopyTime(_Symbol,_Period,0,1,t)<=0)
                 {
                  FileClose(file);
                  return;
                 }
               double current_close[1];
               if(CopyClose(_Symbol,_Period,0,1,current_close)<=0)
                 {
                  FileClose(file);
                  return;
                 }
               string line=StringFormat("%s;%.2f;%.2f;%.2f;%.2f;%.2f;%.2f;%f",TimeToString(t[0],TIME_DATE|TIME_SECONDS),
                                        balance,equity,margin,free_margin,margin_lvl,volume,current_close[0]);
            //---
               FileWrite(file,line);
            //---
               FileClose(file);
              }
            //+------------------------------------------------------------------+
            //| Expert OnTick function                                           |
            //+------------------------------------------------------------------+
            void OnTick()
              {
            //--- trade only at new bar
               if(!IsNewBar())
                  return;
            //--- check trade conditions
               if(SelectPosition())
                  CheckForClose();
               else
                  CheckForOpen();
            //--- save account statistics
               if(SaveStatistics)
                  SaveAccountStatistics();
              }
            //+------------------------------------------------------------------+
            //| Expert deinitialization                                          |
            //+------------------------------------------------------------------+
            void OnDeinit(const int reason)
              {
            //--- save account statistics
               if(SaveStatistics)
                  SaveAccountStatistics();
            //--- release indicator handle
               if(ExtHandle!=INVALID_HANDLE)
                  IndicatorRelease(ExtHandle);
              }
            //+------------------------------------------------------------------+
            

            Test ayarları, parametreler ve sonuçlar Şekil 46-48'de gösterilmiştir.


            Şekil 46. ADXFilteredL1.mq5 Uzman Danışmanı için sınayıcı ayarı

            Şekil 46. ADXFilteredL1.mq5 Uzman Danışmanı için sınayıcı ayarı


            Şekil 47. ADXFilteredL1.mq5 Uzman Danışmanının test parametreleri

            Şekil 47. ADXFilteredL1.mq5 Uzman Danışmanının test parametreleri


            Şekil 48. ADXFilteredL1.mq5 Uzman Danışmanının test sonuçları

            Şekil 48. ADXFilteredL1.mq5 Uzman Danışmanının test sonuçları


            Test için, farklı filtre ayarlarıyla 4 çalıştırma gerçekleştirmeniz gerekir:

            Şekil 49. ADXFilteredL1.mq5 Uzman Danışmanı için sonuçları dosyalara kaydetme parametrelerini test etme

            Şekil 49. ADXFilteredL1.mq5 Uzman Danışmanı için sonuçları dosyalara kaydetme parametrelerini test etme

            Sonrasında, dosyalar sınayıcı dizininde görünecektir; C:\Data'ya kopyalanmalı ve PlotData.py ile işlenmelidir.


            Şekil 50. Farklı filtre modları için ADXFilteredL1.mq5 Uzman Danışmanının test sonuçlarını içeren dosyalar

            Şekil 50. Farklı filtre modları için ADXFilteredL1.mq5 Uzman Danışmanının test sonuçlarını içeren dosyalar



            3.5.3.1. L1 filtrelerini uygulama sonuçları (ADX stratejisi)

            Sonuçlar Şekil 51-53'te gösterilmiştir.

            Şekil 51. Farklı filtre modlarına göre ADXFilteredL1.mq5 Uzman Danışmanı için bakiye grafikleri

            Şekil 51. Farklı filtre modlarına göre ADXFilteredL1.mq5 Uzman Danışmanı için bakiye grafikleri


            Şekil 52. Farklı filtre modlarına göre ADXFilteredL1.mq5 Uzman Danışmanı için varlık grafikleri

            Şekil 52. Farklı filtre modlarına göre ADXFilteredL1.mq5 Uzman Danışmanı için varlık grafikleri


            Şekil 53. Farklı filtre modlarına göre ADXFilteredL1.mq5 Uzman Danışmanı için birleşik Bakiye ve Varlık grafikleri

            Şekil 53. Farklı filtre modlarına göre ADXFilteredL1.mq5 Uzman Danışmanı için birleşik Bakiye ve Varlık grafikleri



            3.5.4. EMA çaprazlamasına dayalı alım-satım stratejisi

            İki Exponential Moving Average (EMA) göstergesinin çaprazlamasına dayalı bir trend takip alım-satım stratejisi uygulayan EMAFilteredL1.mq5 Uzman Danışmanını inceleyelim.

            Strateji iki hareketli ortalama kullanır:

            1. FastEMA - hızlı EMA;
            2. SlowEMA - yavaş EMA.

            İşlem sinyalleri aşağıdaki gibi oluşturulur:

            • ALIŞ: Hızlı EMA, yavaş EMA'yı aşağıdan yukarıya doğru çaprazladığında;
            • SATIŞ: Hızlı EMA, yavaş EMA'yı yukarıdan aşağıya doğru çaprazladığında.

            Analiz için, son iki kapanmış çubuktaki göstergelerin değerleri kullanılır, bu da mevcut tamamlanmamış çubuğun etkisini ortadan kaldırır ve yanlış sinyallerin sayısını azaltır.

            Ayrıca, L1 trendi ayarlarına göre (L1TotalBars, L1FilterOpen, L1FilterClose, L1CoefLambda) işlem giriş ve çıkış filtreleri kullanılır.

            EMAFilteredL1.mq5 Uzman Danışmanının kodu aşağıda sunulmuştur.

            //+------------------------------------------------------------------+
            //|                                                EMAFilteredL1.mq5 |
            //|                             Copyright 2000-2026, MetaQuotes Ltd. |
            //|                                              http://www.mql5.com |
            //+------------------------------------------------------------------+
            #property copyright "Copyright 2000-2026, MetaQuotes Ltd."
            #property link      "https://www.mql5.com"
            #property version   "1.00"
            //--- best EMA parameters for EURUSD,H1,2025
            input int    FastEMA       = 29;     // Fast EMA
            input int    SlowEMA       = 101;     // Slow EMA
            //--- trade volume
            input double TradeLot      = 0.1;    // Lot size
            //--- L1 filter parameters
            input int    L1TotalBars    = 1000;  // Total bars for L1 filter
            input bool   L1FilterOpen  = false;  // Use filter for Open
            input bool   L1FilterClose = false;  // Use filter for Close
            input double L1CoefLambda  = 0.2;    // Lambda in lambda_max units
            //--- save statistics
            input bool   SaveStatistics = false; // Save statistics to file
            //---
            #define EMA_MAGIC 1234503
            #include <Trade\Trade.mqh>
            CTrade ExtTrade;
            int    ExtHandle = INVALID_HANDLE;
            bool   ExtHedging = false;
            int    FastHandle, SlowHandle;
            string ExtStrategyName="EMA";
            string ExtStrategyFileName="";
            //+------------------------------------------------------------------+
            //| Check new bar                                                    |
            //+------------------------------------------------------------------+
            bool IsNewBar()
              {
               static datetime last_time=0;
               datetime t[1];
            //---
               if(CopyTime(_Symbol,_Period,0,1,t)>0)
                 {
                  if(t[0]!=last_time)
                    {
                     last_time=t[0];
                     return(true);
                    }
                 }
               return(false);
              }
            //+------------------------------------------------------------------+
            //| CheckTrendL1                                                     |
            //+------------------------------------------------------------------+
            double CheckTrendL1()
              {
               int max_bars=L1TotalBars;
               MqlRates rates_data[];
               ArrayResize(rates_data,max_bars);
               ArraySetAsSeries(rates_data,false);
               if(CopyRates(_Symbol,_Period,0,max_bars,rates_data) != max_bars)
                 {
                  Print("CopyRates failed for L1Trend");
                  return(0);
                 }
            //--- prepare data (close prices vector)
               int data_count=max_bars;
               vector<double> data_close;
               data_close.Resize(data_count);
               for(int i=0; i<data_count; i++)
                  data_close[i] = rates_data[i].close;
            //--- calculate L1 filter
               vector<double> data_filtered;
               data_filtered.Resize(data_count);
               double dp=0.0;
               bool res=data_close.L1TrendFilter(L1CoefLambda,true,data_filtered);
               if(res)
                  dp = data_filtered[data_count-1] - data_filtered[data_count-2];
            //---
               return(dp);
              }
            //+------------------------------------------------------------------+
            //| GetTradeSignal (2EMA crossover)                                  |
            //+------------------------------------------------------------------+
            bool GetTradeSignal(ENUM_ORDER_TYPE &signal)
              {
               signal=WRONG_VALUE;
            //---
               double fast[],slow[];
               ArrayResize(fast,2);
               ArrayResize(slow,2);
            //---
               ArraySetAsSeries(fast,true);
               ArraySetAsSeries(slow,true);
            //---
               if(CopyBuffer(FastHandle,0,1,2,fast)!=2)
                  return(false);
            //---
               if(CopyBuffer(SlowHandle,0,1,2,slow)!=2)
                  return(false);
            //---
               if(fast[1]<slow[1] && fast[0]>slow[0])
                  signal=ORDER_TYPE_BUY;
               if(fast[1]>slow[1] && fast[0]<slow[0])
                  signal=ORDER_TYPE_SELL;
            //---
               return(true);
              }
            //+------------------------------------------------------------------+
            //| CheckForOpen                                                     |
            //+------------------------------------------------------------------+
            void CheckForOpen()
              {
               ENUM_ORDER_TYPE signal;
            //---
               if(!GetTradeSignal(signal) || signal==WRONG_VALUE)
                  return;
            //---
               if(L1FilterOpen)
                 {
                  double dp=CheckTrendL1();
            //---
                  if(signal==ORDER_TYPE_BUY && dp<0)
                     return;
                  if(signal==ORDER_TYPE_SELL && dp>0)
                     return;
                 }
            //---
               if(!TerminalInfoInteger(TERMINAL_TRADE_ALLOWED) || Bars(_Symbol,_Period)<L1TotalBars)
                  return;
            //---
               double price=(signal==ORDER_TYPE_BUY)
                            ? SymbolInfoDouble(_Symbol,SYMBOL_ASK)
                            : SymbolInfoDouble(_Symbol,SYMBOL_BID);
            //---
               ExtTrade.PositionOpen(_Symbol,signal,TradeLot,price,0,0);
              }
            //+------------------------------------------------------------------+
            //| CheckForClose                                                    |
            //+------------------------------------------------------------------+
            void CheckForClose()
              {
            //--- check position
               if(!PositionSelect(_Symbol))
                  return;
            //--- check position magic
               if(PositionGetInteger(POSITION_MAGIC)!=EMA_MAGIC)
                  return;
            //--- check trade signal
               ENUM_ORDER_TYPE signal;
               if(!GetTradeSignal(signal))
                  return;
            //---
               long type=PositionGetInteger(POSITION_TYPE);
               bool close_signal=false;
            //---
               if(type==POSITION_TYPE_BUY && signal==ORDER_TYPE_SELL)
                  close_signal=true;
            //---
               if(type==POSITION_TYPE_SELL && signal==ORDER_TYPE_BUY)
                  close_signal=true;
            //--- check L1 filter
               if(L1FilterClose)
                 {
                  double dp = CheckTrendL1();
                  if(type == POSITION_TYPE_BUY && dp > 0)
                    {
                     close_signal = false;
                     PrintFormat("Close BUY signal cancelled by L1 trend dp=%.5f", dp);
                    }
                  if(type == POSITION_TYPE_SELL && dp < 0)
                    {
                     close_signal = false;
                     PrintFormat("Close SELL signal cancelled by L1 trend dp=%.5f", dp);
                    }
                 }
            //---
               if(close_signal)
                  ExtTrade.PositionClose(_Symbol,3);
              }
            //+------------------------------------------------------------------+
            //| SelectPosition                                                   |
            //+------------------------------------------------------------------+
            bool SelectPosition()
              {
               bool res = false;
               if(ExtHedging)
                 {
                  uint total = PositionsTotal();
                  for(uint i=0; i<total; i++)
                    {
                     string sym = PositionGetSymbol(i);
                     if(sym == _Symbol && PositionGetInteger(POSITION_MAGIC)==EMA_MAGIC)
                       {
                        res = true;
                        break;
                       }
                    }
                 }
               else
                 {
                  if(PositionSelect(_Symbol))
                     res = (PositionGetInteger(POSITION_MAGIC)==EMA_MAGIC);
                 }
               return(res);
              }
            //+------------------------------------------------------------------+
            //| Expert initialization                                            |
            //+------------------------------------------------------------------+
            int OnInit()
              {
               ExtHedging = (AccountInfoInteger(ACCOUNT_MARGIN_MODE)==ACCOUNT_MARGIN_MODE_RETAIL_HEDGING);
               ExtTrade.SetExpertMagicNumber(EMA_MAGIC);
               ExtTrade.SetMarginMode();
               ExtTrade.SetTypeFillingBySymbol(_Symbol);
            //--- prepare indicators
               FastHandle=iMA(_Symbol,_Period,FastEMA,0,MODE_EMA,PRICE_CLOSE);
               SlowHandle=iMA(_Symbol,_Period,SlowEMA,0,MODE_EMA,PRICE_CLOSE);
               if(FastHandle==INVALID_HANDLE||SlowHandle==INVALID_HANDLE)
                  return(INIT_FAILED);
            //--- prepare filename
               ExtStrategyFileName=PrepareStrategyFileName(ExtStrategyName);
            //--- delete old file if exists
               if(FileIsExist(ExtStrategyFileName))
                  FileDelete(ExtStrategyFileName);
            //---
               return(INIT_SUCCEEDED);
              }
            //+------------------------------------------------------------------+
            //| PrepareStrategyFileName                                          |
            //+------------------------------------------------------------------+
            string PrepareStrategyFileName(string strategy_name)
              {
               int v=0;
               if(L1FilterOpen)
                  v=v | 1;
            //---
               if(L1FilterClose)
                  v=v | 2;
            //---
               string filename=IntegerToString(v)+"_"+strategy_name+"_"+_Symbol+".txt";
               return(filename);
              }
            //+------------------------------------------------------------------+
            //| Save account statistics to file                                  |
            //+------------------------------------------------------------------+
            void SaveAccountStatistics()
              {
            //--- check file name
               if(ExtStrategyFileName=="")
                  return;
            //---
               int file=FileOpen(ExtStrategyFileName,FILE_WRITE|FILE_READ|FILE_TXT|FILE_SHARE_WRITE|FILE_ANSI);
               if(file==INVALID_HANDLE)
                 {
                  Print("File open error: ",GetLastError());
                  return;
                 }
            //--- append
               FileSeek(file,0,SEEK_END);
            //--- account data
               double balance     = AccountInfoDouble(ACCOUNT_BALANCE);
               double equity      = AccountInfoDouble(ACCOUNT_EQUITY);
               double margin      = AccountInfoDouble(ACCOUNT_MARGIN);
               double free_margin = AccountInfoDouble(ACCOUNT_MARGIN_FREE);
               double margin_lvl  = AccountInfoDouble(ACCOUNT_MARGIN_LEVEL);
            //--- volume
               double volume=0.0;
               if(PositionSelect(_Symbol))
                  volume=PositionGetDouble(POSITION_VOLUME);
            //--- time
               datetime t[1];
               if(CopyTime(_Symbol,_Period,0,1,t)<=0)
                 {
                  FileClose(file);
                  return;
                 }
               double current_close[1];
               if(CopyClose(_Symbol,_Period,0,1,current_close)<=0)
                 {
                  FileClose(file);
                  return;
                 }
               string line=StringFormat("%s;%.2f;%.2f;%.2f;%.2f;%.2f;%.2f;%f",TimeToString(t[0],TIME_DATE|TIME_SECONDS),
                                        balance,equity,margin,free_margin,margin_lvl,volume,current_close[0]);
            //---
               FileWrite(file,line);
            //---
               FileClose(file);
              }
            //+------------------------------------------------------------------+
            //| Expert OnTick function                                           |
            //+------------------------------------------------------------------+
            void OnTick()
              {
            //--- trade only at new bar
               if(!IsNewBar())
                  return;
            //--- check trade conditions
               if(SelectPosition())
                  CheckForClose();
               else
                  CheckForOpen();
            //--- save account statistics
               if(SaveStatistics)
                  SaveAccountStatistics();
              }
            //+------------------------------------------------------------------+
            //| Expert deinitialization                                          |
            //+------------------------------------------------------------------+
            void OnDeinit(const int reason)
              {
            //--- save account statistics
               if(SaveStatistics)
                  SaveAccountStatistics();
            //--- release indicator handles
               if(FastHandle!=INVALID_HANDLE)
                  IndicatorRelease(FastHandle);
            //---
               if(SlowHandle!=INVALID_HANDLE)
                  IndicatorRelease(SlowHandle);
              }
            //+------------------------------------------------------------------+

            EMAFilteredL1.mq5 Uzman Danışmanının ayarları, parametreleri ve test sonuçları Şekil 54-56'da gösterilmiştir.

            Şekil 54. EMAFilteredL1.mq5 Uzman Danışmanı için sınayıcı ayarı

            Şekil 54. EMAFilteredL1.mq5 Uzman Danışmanı için sınayıcı ayarı


            Şekil 55. EMAFilteredL1.mq5 Uzman Danışmanının test parametreleri

            Şekil 55. EMAFilteredL1.mq5 Uzman Danışmanının test parametreleri


            Şekil 56. EMAFilteredL1.mq5 Uzman Danışmanının test sonuçları

            Şekil 56. EMAFilteredL1.mq5 Uzman Danışmanının test sonuçları


            L1 filtrelerinin etkinliğini analiz etmek için, Uzman Danışmanı sınayıcıda farklı filtre modlarıyla sırayla çalıştırmak gerekir:

            Şekil 57. EMAFilteredL1.mq5 Uzman Danışmanı için sonuçları dosyalara kaydetme parametrelerini test etme

            Şekil 57. EMAFilteredL1.mq5 Uzman Danışmanı için sonuçları dosyalara kaydetme parametrelerini test etme


            Sonuç olarak, sınayıcı klasöründe dosyalar oluşturulacaktır; bunlar Python komut dosyası PlotData.py'da belirtilen dizine kopyalanmalı ve şu ayarlarla çalıştırılmalıdır: symbol = "EURUSD", name_strategy = "EMA".


            Şekil 58. Farklı filtre modları için EMAFilteredL1.mq5 Uzman Danışmanının test sonuçlarını içeren dosyalar

            Şekil 58. Farklı filtre modları için EMAFilteredL1.mq5 Uzman Danışmanının test sonuçlarını içeren dosyalar



            3.5.4.1. L1 filtrelerini uygulama sonuçları (EMA stratejisi)

            Sonuçlar Şekil 59-61'de gösterilmiştir.

            Şekil 59. Farklı filtre modlarına göre EMAFilteredL1.mq5 Uzman Danışmanı için bakiye grafikleri

            Şekil 59. Farklı filtre modlarına göre EMAFilteredL1.mq5 Uzman Danışmanı için bakiye grafikleri


            Şekil 60. Farklı filtre modlarına göre EMAFilteredL1.mq5 Uzman Danışmanı için varlık grafikleri

            Şekil 60. Farklı filtre modlarına göre EMAFilteredL1.mq5 Uzman Danışmanı için varlık grafikleri


            Şekil 61. Farklı filtre modlarına göre EMAFilteredL1.mq5 Uzman Danışmanı için birleşik Bakiye ve Varlık grafikleri

            Şekil 61. Farklı filtre modlarına göre EMAFilteredL1.mq5 Uzman Danışmanı için birleşik Bakiye ve Varlık grafikleri




            3.5.5. Moving Average, MACD, ADX ve EMA alım-satım stratejilerinde L1 filtresinin kullanımına ilişkin özet

            Ele alınan örneklerde, alım-satım stratejileri EURUSD döviz paritesinde, H1 zaman diliminde, 2025 yılı için test edilmiştir (Şekil 62).

            Şekil 62. Test dönemi boyunca EURUSD fiyat grafiği (2025, H1, kapanış fiyatları)

            Şekil 62. Test dönemi boyunca EURUSD fiyat grafiği (2025, H1, kapanış fiyatları)


            Şekil 63. Farklı filtre modlarına göre Moving Average, MACD, ADX, ve EMA stratejileri için bakiye grafikleri

            Şekil 63. Farklı filtre modlarına göre Moving Average, MACD, ADX, ve EMA stratejileri için bakiye grafikleri


            Moving Average, MACD, ADX ve EMA stratejilerinin analizi, en iyi sonuçların pozisyon kapatma aşamasında L1 filtreleme uygulandığında elde edildiğini göstermiştir (grafiklerde yeşil renkle vurgulanmıştır). Çıkışta filtrenin kullanılması, gürültü geri dönüşlerini ve yanlış sinyalleri etkili bir şekilde bastırarak pozisyonların istikrarlı bir trend yönünde tutulmasına olanak tanır. Bu, karın ve Kar Faktörünün artmasının yanı sıra maksimum düşüşün azalmasına yol açar.

            Pozisyon açma aşamasında L1 filtrelemenin kullanılmasının (turuncu ile vurgulanmıştır) daha az etkili olduğu kanıtlanmıştır, çünkü ek filtreleme giriş sayısını sınırlar ve işlem kalitesinde orantılı bir iyileşme olmaksızın karlı hareketlerin bir kısmının kaçırılmasına neden olur.


            Şekil 64. Farklı filtre modlarına göre Moving Average, MACD, ADX, ve EMA stratejileri için birleşik Bakiye ve Varlık grafikleri

            Şekil 64. Farklı filtre modlarına göre Moving Average, MACD, ADX, ve EMA stratejileri için birleşik Bakiye ve Varlık grafikleri


            Böylece, pozisyon kapanışında işlem sinyallerinin L1 filtrelemesi alım-satım sisteminin istikrarını artırır, kısa vadeli fiyat dalgalanmalarına duyarlılığı azaltır ve kar/risk oranını iyileştirir. Klasik hareketli ortalamalara kıyasla, L1 filtresi geçici düzeltmeleri gerçek trend dönüşlerinden daha iyi ayırt ederek trend takip stratejilerinin daha verimli kullanılmasını sağlar.

            L1 trend filtresiyle uyumlu sinyallerle işlem yaparken Bakiye ve Varlık eğrilerinin davranışına dikkat edilmelidir (Şekil 64). Trend boyunca işlem yaparken, Varlık genellikle Bakiyenin üzerindedir, bu da risk ölçütlerini önemli ölçüde iyileştirir ve düşüşü azaltır. Bu nedenle, trendle uyumlu hareket etmek, alım-satım sistemlerinin özelliklerini de iyileştirir (düşüşü ve riskleri azaltır).

            Ek olarak, işlem sinyalleri L1 trendiyle uyumlu hale getirildiğinde, işlem sayısı azalırken kaliteleri artar ve bu da alım-satım stratejilerinin genel istatistiksel özelliklerini olumlu yönde etkiler.


            Strateji Toplam Net Kar, USD % Al ve Tut
            1  Al ve Tut 1363.8 %100
            2  Moving Average (filtre yok) 1001.03 %73.4
            3  Moving Average (L1 giriş filtresi) 107.65 %7.89
            4  Moving Average (L1 çıkış filtresi) 1342.5 %98.43
            5  Moving Average (L1 giriş + çıkış filtresi) 986.16 %72.31
            6  MACD (filtre yok) 997.79 %73.16
            7  MACD (L1 giriş filtresi) 140.13 %10.27
            8  MACD (L1 çıkış filtresi) 1359.52 %99.69
            9  MACD (L1 giriş + çıkış filtresi) 697.54 %51.15
            10  ADX (filtre yok) 791.99 %58.07
            11  ADX (L1 giriş filtresi) -50.9 %-3.73
            12  ADX (L1 çıkış filtresi) 940.39 %68.95
            13  ADX (L1 giriş + çıkış filtresi) 430.05 %31.53
            14  EMA (filtre yok) 957.3 %70.19
            15  EMA (L1 giriş filtresi) -173.35 %-12.71
            16  EMA (L1 çıkış filtresi) 1258.99 %92.31
            17  EMA (L1 giriş + çıkış filtresi) -131.41 %-9.64

            Tablo 4. Moving Average, MACD, ADX ve EMA stratejilerinde L1 filtresi kullanımının Al ve Tut'a kıyasla toplam kar sonuçları


            Tablo 4'e göre, pozisyon kapanışında L1 filtresinin kullanılması tüm stratejiler için karlılığı artırmıştır.

            Al ve Tut stratejisinin sonucu (1363.8$) tüm trend hareketinin %100'ü olarak alınırsa, şunları elde ederiz:

            1. Moving Average karı %73.4'ten %98.43'e yükseldi;
            2. MACD karı %73.16'dan %99.69'a yükseldi;
            3. ADX karı %58.07'den %68.5'e yükseldi;
            4. EMA karı %70.19'dan %92.31'e yükseldi.

            Gördüğümüz gibi, L1 filtresinin kullanımı Moving Average, MACD ve EMA stratejilerinin karını %22-26 oranında artırmasına, trend hareketinin çoğunu yakalamasına (%98.43, %99.69 ve %92.31) ve Al ve Tut sonucuna yaklaşmasına olanak sağladı. ADX stratejisi karı %10 oranında arttı.

            Örneklerde, en yüksek bakiye değerlerini veren (yani, mümkün olan en iyi çözümler arasında yer alan) parametrelere sahip stratejiler dikkate alınmıştır. Bunlar mavi renkle vurgulanmıştır. Sonuçlar, bu en karlı çözümlerin bile L1 trendiyle uyumlu hale getirilerek işlem sinyallerinin ilave filtrelenmesiyle daha da iyileştirilebileceğini göstermektedir. Bazı stratejiler çok az iyileşme göstermiştir (ADX için yeşil eğriler mavilere yakındır, bu da optimum bakiye çözümüne yakınlığı ifade eder). Bir stratejinin işlem sinyallerinin kalitesi (seçilen parametrelerin idealliği), bu tür bir L1 filtreleme ile ne kadar iyileştiğine göre değerlendirilebilir.

            Özellikle, bu durumda güçlü bir trend gösteren EURUSD piyasası dikkate alınmıştır (Şekil 62). Diğer piyasa rejimleri ve enstrümanlar için sonuçlar farklılık gösterecektir. Ayrıca, L1 trendi H1 zaman diliminde λ = 0.2·λmax düzenlileştirme parametresi ile oluşturulmuştur. Diğer enstrümanlar ve zaman dilimleri için, bu katsayının uygun değerleri L1 trendi göstergeleri kullanılarak hesaplanabilir.


            Sonuç

            L1 trend filtreleme, yerel gürültüyü gerçek trend değişikliklerinden ayırmak için bir araç olarak pratik yararlılığını kanıtlamıştır.

            Yöntem, otomatik kırılma noktaları ve λmax aracılığıyla uygun bir ayar ölçeği ile parçalı doğrusal bir trend oluşturur ve bu da manuel parametre uyarlama sorununu ortadan kaldırır.

            Pratik entegrasyon düzeyinde, eksiksiz bir araç seti sağlanmıştır: λmax ve L1 filtresini hesaplamak için fonksiyonlar, üç gösterge (L1Trend, L1TrendSlope, L1TrendSlopeSign), yedi L1 trendi volatilite göstergesi (L1Volatility, L1VolatilitySmoothed, L1VolatilityAbsolute, L1VolatilityNormalized, L1VolatilityNormalizedSmoothed, L1VolatilityRegime, L1VolatilityRegimeColor), Uzman Danışman şablonları ve tekrarlanabilir bir test protokolü (dört mod: filtre yok, giriş filtresi, çıkış filtresi, her iki filtre; sonuçları kaydetme ve Python görselleştirme komut dosyası).

            L1 trend filtresinin makine öğreniminde veri etiketleme için kullanılabileceği de unutulmamalıdır. Özellikle, “Makine Öğrenimini Kullanarak Trend Alım-Satım Stratejileri Geliştirme” makalesinde, trend belirleme, Savitzky-Golay filtresi ile yumuşatılmış fiyatların türevleri kullanılarak gerçekleştirilir. Benzer bir yaklaşım, trendin parçalı doğrusal fonksiyonlarla yaklaştırıldığı ve her bir segmentteki trendin gücünün doğal olarak ilgili segmentin eğimiyle ilişkili olduğu L1 filtreleme kullanılarak uygulanabilir.


            Pratik öneriler:

            • Göreceli düzenlileştirme kullanın: λ = coef_lambda_max · λmax. Çoğu görev için 0.04-0.25 aralığında katsayı kullanın; daha ince ayrıntılar için ≈0.02-0.04; kaba yaklaşma ve rejim tespiti için ≈0.12-0.25.
            • Çoğu durumda, L1 filtresi en çok pozisyon kapatmaya uygulandığında etkilidir (karlı trendleri korumak ve erken çıkışları azaltmak). Bunu girişe uygulamak, genellikle kalitede orantılı bir iyileşme olmaksızın işlem sayısını azaltır.
            • Mevcut trend analizi için basit bir kural kullanın: delta = x_filtered[last] - x_filtered[last-1]. Deltanın işareti baskın L1 trendinin yönünü gösterir.

            Sınırlama: etki enstrümana, zaman dilimine ve piyasa rejimine bağlıdır; seçilen ölçütlerle geçmiş veriler üzerinde doğrulama gereklidir.

            Sunulan MQL5 modülleri ve test protokolü, belirli bir alım-satım sistemi için hızlı hipotez testi ve çalışma parametrelerinin seçilmesine olanak tanır.

            Makaledeki tüm kodlar "MQL5\Shared Projects\L1Trend" herkese açık projesinde de mevcuttur.


            Örnekler

            Tür Dosya Açıklama
            Komut dosyası MQL5\Scripts\TestL1Trend.mq5
            Model verilerinde (rastgele yürüyüş) L1 trendini hesaplamak için test komut dosyası
            Komut dosyası MQL5\Scripts\TestL1TrendFloatDouble.mq5 Double ve float vektörler için model verilerinde (rastgele yürüyüş) L1 trendini hesaplamak için test komut dosyası
            Komut dosyası MQL5\Scripts\TestL1TrendFilterSP500.mq5 SP500 endeksi fiyat verilerinde L1 trendini hesaplamak için test komut dosyası
            Veri dosyası MQL5\Files\snp500.txt
            Test komut dosyası için veri dosyası (SP500 endeksi fiyat serisinin logaritması)
            Komut dosyası MQL5\Scripts\TestScalingBrownianMotion.mq5 Brown hareketi için λmax'ın kuvvet yasası bağımlılığını hesaplayan komut dosyası
            Komut dosyası MQL5\Scripts\TestScalingSymbol.mq5 Belirli bir sembolün fiyat serisi için λmax'ın kuvvet yasası bağımlılığını hesaplayan komut dosyası
            Gösterge MQL5\Indicators\L1TrendFilter.mq5 L1 trendini hesaplamak için gösterge
            Gösterge
            MQL5\Indicators\L1TrendFilter_Slope.mq5 L1 trendinin değişim oranını hesaplamak için gösterge
            Gösterge
            MQL5\Indicators\L1TrendFilter_SlopeSign.mq5 L1 trendinin değişim işaretini hesaplamak için gösterge
            Gösterge
            MQL5\Indicators\L1Volatility.mq5 Artık volatiliteyi (kapanış fiyatları ile L1 trend değeri arasındaki fark) hesaplamak için gösterge
            Gösterge
            MQL5\Indicators\L1VolatilitySmoothed.mq5 Yumuşatılmış artık volatiliteyi hesaplamak için gösterge
            Gösterge
            MQL5\Indicators\L1VolatilityAbsolute.mq5 Mutlak volatiliteyi hesaplamak için gösterge
            Gösterge
            MQL5\Indicators\L1VolatilityNormalized.mq5 ATR (Average True Range) ve L1 trendini kullanarak normalleştirilmiş volatiliteyi hesaplamak için gösterge
            Gösterge
            MQL5\Indicators\L1VolatilityNormalizedSmoothed.mq5 Yumuşatılmış normalleştirilmiş volatiliteyi hesaplamak için gösterge
            Gösterge
            MQL5\Indicators\L1VolatilityRegime.mq5 Piyasa rejimi tespit göstergesi
            Gösterge
            MQL5\Indicators\L1VolatilityRegimeColor.mq5 Piyasa rejimi tespit göstergesinin renkli versiyonu
            Uzman Danışman MQL5\Experts\MovingAverageFilteredL1.mq5 L1 filtreli Moving Average stratejisine dayalı Uzman Danışman
            Uzman Danışman
            MQL5\Experts\MACDFilteredL1.mq5 L1 filtreli MACD stratejisine dayalı Uzman Danışman
            Uzman Danışman MQL5\Experts\ADXFilteredL1.mq5 L1 filtreli ADX stratejisine dayalı Uzman Danışman
            Uzman Danışman MQL5\Experts\EMAFilteredL1.mq5 L1 filtreli iki EMA'nın çaprazlamasına dayalı Uzman Danışman
            Python komut dosyası MQL5\Scripts\PlotData.py İşlem sinyallerine L1 filtresi uygulamanın etkinliğini analiz etmek için Python komut dosyası

            Tablo 5. Makalede kullanılan program kodlarının açıklaması



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

            Ekli dosyalar |
            Son yorumlar | Tartışmaya git (8)
            Renat Akhtyamov
            Renat Akhtyamov | 20 Nis 2026 saat 19:09

            Şöyle bir şey:


            Quantum
            Quantum | 20 Nis 2026 saat 20:12
            Renat Akhtyamov #:

            Şöyle bir şey:

            Trendlere ayırma işlemi, lambda düzenleme parametresine büyük ölçüde bağlıdır - lambda ne kadar küçükse, o kadar kısa trendleri yakalayabilir.
            İncelenen örneklerde, lambda=0.2*lambda_max birimlerinde sabit lambda değerleri kullanılmıştır. Lambda_max birimlerinde hesaplama, verilere kısmen uyum sağlamaya izin verir. Lambda_max değerinin kendisi, serinin geometrisine (göreceli dalgalanmalara), yani oynaklığa bağlıdır.

            Trendin farklı aşamaları ve kendi yaşam döngüsü olduğunu akılda tutmak gerekir. Bu nedenle , mevcut trende uyum sağlamak için bir tür mekanizma gereklidir, yani lambda'yı bir şekilde yönetmek ve optimal trend bölünmesini bulmak - bu görev henüz çözülmemiştir.

            Stratejinin kendisi zaman aralığında kâr sağlamıyorsa, filtre de yardımcı olamaz.
            En iyi sonuçlar ideal bir trend piyasasında elde edilmelidir, örnek şuydu: EURUSD, 2025, H1 (en iyi parametreler MovingAverage period=61).

            EURUSD

            Test seçenekleri

            Test parametreleri

            Filtre uygulanmamış test sonuçları

            L1 Close filtresi

            L1 Filtreyi Kapat

            Burada, çıkış filtresinin trendli bölümde kârı artırmaya yardımcı olduğunu görüyoruz.


            Aynı stratejinin, düzeltmelerde pozisyonların eklenmesiyle birlikte bir varyantı:

            Seçenekleri ekleyerek test et

            Pozisyon ekleme ile test seçenekleri

            eklemeler olmadan:

            Eklemelerle:


            Düz piyasa aralıkları, yerel küçük trendler içerir ve bunları doğru bir şekilde hesaba katmak için, lambda parametresinin daha küçük değerlerini kullanmak gerekir (çıkış filtresi olarak kullanıldığında).
            Ayrıca, düz piyasa aralığında MovingAverage parametrelerinin en iyi değerleri de farklı olmalıdır. Yani, ikinci aralıktaki ortalama dönemleri değişmiştir (ancak test cihazında optimizasyon sırasında bulunan parametreler, tüm optimizasyon aralığı içinde diğer tüm parametreler arasında en yüksek kârı sağlar).

            Quantum
            Quantum | 20 Nis 2026 saat 21:08

            Farklı lambda değerleriyle flat aralığındaki sonuçları inceleyelim.

            Filtreler olmadan:

            Çıkış filtresi lambda=0.2*lambda_max ile

            Filtre ile lambda=0.001 lambda_max (daha küçük trendler)

            Böylece, lambda=0.001 lambda_max değerinde düz aralıkta, filtreler olmadan elde edilen sonucu iyileştirmek ve yerel küçük trendleri hesaba katmak mümkün oluyor.

            Ancak, lambda=0,2*lambda_max filtreli seçenek, burada filtre kullanılmayan stratejiye göre daha düşük karlılık göstermiştir.

            Quantum
            Quantum | 20 Nis 2026 saat 21:30

            Yatay seyrin içindeki yerel trendlerde pozisyon ekleme seçeneği (farklı lambda değerleri)

            Filtreler olmadan:


            lambda=0.2*lambda_max filtresi ve düzeltmelerde ekleme ile:

            lambda=0.001*lambda_max filtresi ve düzeltmelere ekleme ile:

            lambda=0.2*lambda_max filtresi ve düzeltmelerde ekleme seçeneği, filtreler olmadan seçenekten daha iyi sonuç verdi.

            Yatay aralığın içindeki yerel küçük trendlerin (lambda=0.001*lambda_max) düzeltmelerine ekleme yapılması, filtre içermeyen orijinal stratejinin kârını artırdı (ve lambda=0.2*lambda_max seçeneğinin kârını iyileştirdi).

            Renat Akhtyamov
            Renat Akhtyamov | 21 Nis 2026 saat 08:06
            Quantum #:

            Yatay hareket içindeki yerel trendlerde pozisyon ekleme seçeneği (farklı lambda)

            Filtreler olmadan:


            lambda=0.2*lambda_max filtresi ve düzeltmelerde ekleme ile:

            lambda=0.001*lambda_max filtresiyle ve düzeltmelere eklenerek:

            lambda=0.2*lambda_max filtreli ve düzeltmelere ekleme yapılan seçenek, filtresiz seçeneğe göre daha iyi sonuç verdi.

            Flat aralığı içindeki küçük yerel trendlerin (lambda=0.001*lambda_max) düzeltmelere eklenmesi, filtre içermeyen orijinal stratejinin kârını artırdı (ve lambda=0.2*lambda_max seçeneğinin kârını iyileştirdi).

            En azından demo hesabında işlem yapın

            Deneyim kazandıkça anlayacaksınız, 10 ay boyunca sonuçsuz çalışmanın ardından

            Yeni Raylara Adım Atın: MQL5'te Özel Göstergeler Yeni Raylara Adım Atın: MQL5'te Özel Göstergeler
            Yeni terminalin ve dilin tüm yeni olanaklarını ve özelliklerini listelemeyeceğim. Bunlar sayısızdır ve bazı yenilikler ayrı bir makalede tartışılmaya değerdir. Ayrıca burada nesne yönelimli programlama ile yazılmış bir kod yoktur, geliştiriciler için ek avantajlar olarak bir bağlamda basitçe bahsedilemeyecek kadar ciddi bir konudur. Bu makalede, MQL4'e kıyasla göstergeleri, yapılarını, çizimlerini, türlerini ve programlama ayrıntılarını ele alacağız. Umarım bu makale hem yeni başlayanlar hem de deneyimli geliştiriciler için faydalı olacaktır, belki bazıları yeni bir şeyler bulacaktır.
            Forex'te portföy optimizasyonu: VaR ve Markowitz teorisinin birleştirilmesi Forex'te portföy optimizasyonu: VaR ve Markowitz teorisinin birleştirilmesi
            Forex'te portföy alım-satımı nasıl çalışır? Portföy dağılımı optimizasyonu için Markowitz portföy teorisi ve portföy risk optimizasyonu için VaR modeli nasıl birleştirilebilir? Bir yandan düşük risk, diğer yandan kabul edilebilir uzun vadeli karlılık elde edeceğimiz portföy teorisine dayalı bir kod oluşturuyoruz.
            İşte Karışınızda Yeni MetaTrader 5 ve MQL5 İşte Karışınızda Yeni MetaTrader 5 ve MQL5
            Bu MetaTrader 5 ile ilgili sadece kısa bir inceleme. Sistemin tüm yeni özelliklerini bu kadar kısa sürede açıklayamam, test süreci 09.09.2009’da başladı. Bu sembolik bir tarihtir ve şanslı sayı olacağına eminim. MetaTrader 5 terminalinin ve MQL5’in beta sürümünü beş gün önce aldım. Tüm özelliklerini deneme şansım olmadı ama şimdiden etkilendim.
            3D geri dönüş formasyonlarına dayalı algoritmik alım-satım 3D geri dönüş formasyonlarına dayalı algoritmik alım-satım
            3D çubuklarda yeni bir otomatik alım-satım dünyasını keşfediyoruz. Bir alım-satım robotu çok boyutlu fiyat çubuklarında neye benziyor? "Sarı" renkli 3D çubuk kümeleri trend dönüşlerini tahmin edebilir mi? Çok boyutlu alım-satım nasıl bir şey?