English Русский 中文 Español Deutsch 日本語 Português Italiano
preview
MetaTrader 5'i kullanarak Python'da özel döviz paritesi formasyonları bulma

MetaTrader 5'i kullanarak Python'da özel döviz paritesi formasyonları bulma

MetaTrader 5Alım-satım |
11 1
Yevgeniy Koshtenko
Yevgeniy Koshtenko

Forex formasyon analizine giriş

Yeni başlayanlar döviz paritesi grafiklerine ilk baktıklarında ne görürler? Çok sayıda gün içi dalgalanma, volatilite artışları ve azalışları, trend değişiklikleri ve çok daha fazlası. İnişler, çıkışlar, zikzaklar - hepsini nasıl anlayacaksınız? Ayrıca Forex ile tanışmaya kendimi fiyat formasyonu analizi çalışmasına kaptırarak başladım.

Dünyamızdaki pek çok şey ilk bakışta kaotik görünmektedir. Ancak deneyimli her uzman, kendi kişisel alanında başkalarına kafa karıştırıcı görünen kalıplar ve olasılıklar görür. Aynı durum döviz paritesi grafikleri için de geçerlidir. Bu kaosu sistematik hale getirmeye çalışırsak, gelecekteki fiyat hareketlerini önerebilecek gizli formasyonları keşfedebiliriz.

Ama onları nasıl bulacağız? Gerçek bir formasyonu rastgele gürültüden nasıl ayırt edebiliriz? İşte eğlence burada başlıyor. Python ve MetaTrader 5'i kullanarak kendi formasyon analiz sistemimi oluşturmaya karar verdim. Forex'i fethetmek için matematik ve programlamanın bir tür ortak yaşamı.

Buradaki fikir, tekrar eden formasyonları bulacak ve performanslarını değerlendirecek bir algoritma kullanarak çok sayıda geçmiş veriyi incelemekti. Kulağa ilginç geliyor mu? Gerçekte, uygulamanın o kadar da basit olmadığı ortaya çıktı.


Ortamın kurulması: Gerekli kütüphanelerin yüklenmesi ve MetaTrader 5'e bağlanma

İlk görevimiz Python'ı yüklemektir. Resmi web sitesi python.org'dan indirilebilir. "Add Python to PATH" kutusunu işaretlediğinizden emin olun. 

Bir sonraki önemli adım kütüphanelerdir. Birkaç tanesine ihtiyacımız olacak. Ana olan MetaTrader 5'tir. Ayrıca, verilerle çalışmak için 'pandas' vardır. Ve 'numpy'. Komut satırını açın ve şunu yazın:

pip install MetaTrader5 pandas numpy matplotlib pytz

Yapmanız gereken ilk şey MetaTrader 5'in kendisini yüklemektir. Aracı kurumunuzun resmi web sitesinden indirin ve yükleyin. Karmaşık bir şey değil.

Şimdi terminale giden yolu bulmamız gerekiyor. Tipik olarak, "C:\Program Files\MetaTrader 5\terminal64.exe" gibi bir şeydir.

Şimdi Python'ı açın ve şunları yazın:

import MetaTrader5 as mt5

if not mt5.initialize(path="C:/Program Files/MetaTrader 5/terminal64.exe"):
    print("MetaTrader 5 initialization failed.")
    mt5.shutdown()
else:
    print("MetaTrader 5 initialized successfully.")

Başlatın. Başarılı terminal başlatma mesajını görürseniz, her şey doğru yapılmış demektir.

Her şeyin çalıştığından emin olmak ister misiniz? Biraz veri toplamaya çalışalım:

import MetaTrader5 as mt5
import pandas as pd
from datetime import datetime

if not mt5.initialize():
    print("Oops! Something went wrong.")
    mt5.shutdown()

eurusd_ticks = mt5.copy_ticks_from("EURUSD", datetime.now(), 10, mt5.COPY_TICKS_ALL)
ticks_frame = pd.DataFrame(eurusd_ticks)
print("Look at this beauty:")
print(ticks_frame)

mt5.shutdown()

Eğer bir veri tablosu görüyorsanız, tebrikler! Python’ı kullanarak algoritmik Forex alım-satımı dünyasına ilk adımınızı attınız. Göründüğü kadar zor değil.


Kod yapısı: Temel fonksiyonlar ve amaçları

Kod yapısını analiz etmeye başlayalım. Bu, Forex piyasasındaki formasyonları analiz etmek için eksiksiz bir sistemdir. 

Sistemdeki ana şeyle başlayacağız - find_patterns fonksiyonu. Bu fonksiyon geçmiş verileri inceleyerek belirli bir uzunluktaki formasyonları tanımlar. İlk formasyonları bulduktan sonra, bunların verimliliğini değerlendirmemiz gerekir. Bu fonksiyon aynı zamanda gelecekte kullanılmak üzere son formasyonu da hatırlar.

Bir sonraki fonksiyon calculate_winrate_and_frequency'dir. Bu fonksiyon bulunan formasyonları analiz eder - oluşma sıklığı, kazanç oranı ve formasyonların sıralanması.

process_currency_pair fonksiyonu da önemli bir rol oynar. Bu oldukça önemli bir işleyicidir. Verileri yükler, inceler, farklı uzunluklardaki formasyonları arar ve satışlar ve alışlar için en iyi 300 formasyonu belirler. Kodun başlangıcında ise başlatma, parametre ayarları, grafik zaman dilimi ve zaman aralığı (benim durumumda 1990'dan 2024'e kadar) yer almaktadır.

Şimdi ana kod yürütme döngüsüne geçelim. Formasyon arama algoritmasının özellikleri farklı formasyon uzunluklarını içerir, çünkü kısa olanlar yaygındır ancak güvenilirlik sağlamaz, uzun olanlar ise daha etkili olmalarına rağmen çok nadirdir. Tüm boyutları göz önünde bulundurmalıyız.


MetaTrader 5'ten verileri alma: copy_rates_range fonksiyonu

İlk fonksiyonumuz terminalden verileri almalıdır. Koda bir göz atalım:

import MetaTrader5 as mt5
import pandas as pd
import time
from datetime import datetime, timedelta
import pytz

# List of major currency pairs
major_pairs = ['EURUSD']

# Setting up data request parameters
timeframe = mt5.TIMEFRAME_H4
start_date = pd.Timestamp('1990-01-01')
end_date = pd.Timestamp('2024-05-31')

def process_currency_pair(symbol):
    max_retries = 5
    retries = 0
    while retries < max_retries:
        try:
            # Loading OHLC data
            rates = mt5.copy_rates_range(symbol, timeframe, start_date, end_date)
            if rates is None:
                raise ValueError("No data received")
            ohlc_data = pd.DataFrame(rates)
            ohlc_data['time'] = pd.to_datetime(ohlc_data['time'], unit='s')
            break
        except Exception as e:
            print(f"Error loading data for {symbol}: {e}")
            retries += 1
            time.sleep(2)  # Wait before retrying

    if retries == max_retries:
        print(f"Failed to load data for {symbol} after {max_retries} attempts")
        return

    # Further data processing...

Bu kodda neler oluyor? İlk olarak, döviz paritelerimizi tanımlıyoruz. Şu anda sadece EURUSD var, ancak başka döviz pariteleri de ekleyebilirsiniz. Ardından zaman dilimini ayarlıyoruz. H4, 4 saattir. Bu optimal zamandır. 

Sırada tarihler var. 1990'dan 2024'e kadar. Çok sayıda geçmiş fiyata ihtiyacımız olacak. Elimizde ne kadar çok veri olursa, analizimiz de o kadar doğru olur. Şimdi asıl konuya gelelim - process_currency_pair fonksiyonu. copy_rates_range fonksiyonunu kullanarak verileri yükler.

Sonuç olarak ne elde ediyoruz? Geçmiş verileri içeren bir DataFrame. Zaman, Açılış, Kapanış, Yüksek, Düşük - çalışma için gerekli olan her şey.

Bir şeyler ters giderse, hatalar tespit edilir, ekranda görüntülenir ve tekrar deneriz.


Zaman serisi işleme: OHLC verilerini fiyat hareketi yönlerine dönüştürme

Ana görevimize geri dönelim. Forex piyasasının kaotik dalgalanmalarını daha düzenli bir şeye dönüştürmek istiyoruz - trendler ve terse dönüşler. Bunu nasıl yapacağız? Fiyatları yönlere çevireceğiz.

İşte kodumuz:

# Fill missing values with the mean
ohlc_data.fillna(ohlc_data.mean(), inplace=True)

# Convert price movements to directions
ohlc_data['direction'] = np.where(ohlc_data['close'].diff() > 0, 'up', 'down')

Burada neler oluyor? İlk olarak, fiyat boşluklarını (gap’leri) dolduruyoruz. Fiyat boşlukları nihai sonucumuzu önemli ölçüde kötüleştirebilir. Bunları ortalama değerlerle dolduruyoruz. 

Ve şimdi en ilginç kısım. 'direction' adında yeni bir sütun oluşturuyoruz. Burada fiyat verilerini trend davranışını simüle eden verilere dönüştürüyoruz. Temel bir şekilde çalışır:

  • Mevcut kapanış fiyatı önceki kapanış fiyatından yüksekse, 'up' yazıyoruz
  • Mevcut kapanış fiyatı önceki kapanış fiyatından düşükse, 'down' yazıyoruz.

Oldukça basit bir formülasyon, ancak oldukça etkili. Artık karmaşık sayılar yerine basit bir 'up' ve 'down' dizisine sahibiz. Bu dizi insan algısı için çok daha kolaydır. Ama buna neden ihtiyacımız var? Bu 'up' ve 'down' değerleri formasyonlarımızın yapı taşlarıdır. Onlardan piyasada neler olup bittiğine dair eksiksiz bir resim toplayacağız.


Formasyon arama algoritması: find_patterns fonksiyonu

Elimizde 'up' ve 'down' değerlerini içeren bir dizi var. Şimdi, bu dizide tekrar eden formasyonları arayacağız.

İşte find_patterns fonksiyonu:

def find_patterns(data, pattern_length, direction):
    patterns = defaultdict(list)
    last_pattern = None
    last_winrate = None
    last_frequency = None

    for i in range(len(data) - pattern_length - 6):
        pattern = tuple(data['direction'][i:i+pattern_length])
        if data['direction'][i+pattern_length+6] == direction:
            patterns[pattern].append(True)
        else:
            patterns[pattern].append(False)

    # Check last prices for pattern match
    last_pattern_tuple = tuple(data['direction'][-pattern_length:])
    if last_pattern_tuple in patterns:
        last_winrate = np.mean(patterns[last_pattern_tuple]) * 100
        last_frequency = len(patterns[last_pattern_tuple])
        last_pattern = last_pattern_tuple

    return patterns, last_pattern, last_winrate, last_frequency

Tüm bunlar nasıl çalışıyor?

  • 'patterns' sözlüğünü oluşturuyoruz. Bu, bulduğumuz tüm formasyonları saklayacağımız bir tür kütüphane görevi görecektir.
  • Ardından veriler üzerinde döngü gerçekleştiriyoruz. pattern_length (25'e kadar 3, 4, 5 vb. olabilir) uzunluğunda bir veri örneklemi alıyoruz ve ondan 6 çubuk sonra ne olduğuna bakıyoruz.
  • 6 çubuktan sonra fiyat istenen yönde hareket ediyorsa (alış formasyonları için yukarı yönde veya satış formasyonları için aşağı yönde), True olarak ayarlıyoruz. Aksi taktirde False olarak ayarlıyoruz.
  • Bunu tüm olası veri örneklemleri için yapıyoruz. Benzer formasyonlar elde etmeliyiz: "up-up-down" - True, "down-up-up" - False vb.
  • Sonrasında, daha önce karşılaştığımız formasyonlardan herhangi birinin şu anda oluşup oluşmadığını kontrol ediyoruz. Eğer öyleyse, kazanç oranını (başarılı sonuçların yüzdesi) ve oluşma sıklığını hesaplıyoruz.

Basit bir 'up' ve 'down' dizisini bu şekilde oldukça güçlü bir tahmin aracına dönüştürüyoruz. Ama hepsi bu kadar değil. Daha sonra, bu formasyonları sıralayacak, en verimli olanları seçecek ve analiz edeceğiz.


Formasyon istatistiklerinin hesaplanması: Kazanç oranı ve oluşma sıklığı

Artık elimizde çok sayıda formasyon olduğuna göre, en iyilerini seçmemiz gerekiyor.

Kodumuza bir göz atalım:

def calculate_winrate_and_frequency(patterns):
    results = []
    for pattern, outcomes in patterns.items():
        winrate = np.mean(outcomes) * 100
        frequency = len(outcomes)
        results.append((pattern, winrate, frequency))
    results.sort(key=lambda x: x[1], reverse=True)
    return results

Burada her bir formasyonu ve sonuçlarını (daha önce True ve False olarak tanımlamıştık) alıyor ve ardından kazanç oranını hesaplıyoruz - bu bizim verimlilik yüzdemizdir. Eğer bir formasyon 10 üzerinden 7 kez çalışıyorsa, kazanç oranı %70'tir. Ayrıca sıklığı da sayıyoruz - bu, formasyonun kaç kez meydana geldiğidir. Ne kadar sık olursa, istatistiklerimiz o kadar güvenilir olur. Tüm bunları 'results' listesine koyuyoruz. Ve son olarak, sıralama yapıyoruz. En iyi formasyonları listenin başına koyuyoruz.


Sonuçları filtreleme: Önemli formasyonların seçilmesi

Artık elimizde yeterince veri var. Ancak hepsine ihtiyacımız olmayacak. Bunları filtrelememiz gerekiyor.

filtered_buy_results = [result for result in all_buy_results if result[2] > 20]
filtered_sell_results = [result for result in all_sell_results if result[2] > 20]

filtered_buy_results.sort(key=lambda x: x[1], reverse=True)
top_300_buy_patterns = filtered_buy_results[:300]

filtered_sell_results.sort(key=lambda x: x[1], reverse=True)
top_300_sell_patterns = filtered_sell_results[:300]

İlk olarak, 20 defadan az meydana gelen tüm formasyonları ayıklıyoruz. İstatistiklerin de gösterdiği gibi, nadir görülen formasyonlar daha az güvenilirdir. 

Daha sonra kalan formasyonları kazanç oranına göre sıralıyoruz. En verimli olanlar listenin başında yer alır. Sonuç olarak, ilk 300'ü seçiyoruz. Sayıları bini aşan çok sayıda formasyondan geriye kalması gerekenler bunlardır.  


Farklı formasyon uzunlukları ile çalışma: 3'ten 25'e kadar

Şimdi, alım-satım yaparken istatistiksel olarak ve tutarlı bir şekilde kar üretecek formasyon varyasyonlarını seçmemiz gerekiyor. Seçenekler uzunluk bakımından farklılık gösterir. Bunlar 3 ya da 25 fiyat hareketinden oluşabilir. Tüm olası olanları kontrol edelim:

pattern_lengths = range(3, 25)  # Pattern lengths from 3 to 25
all_buy_patterns = {}
all_sell_patterns = {}

for pattern_length in pattern_lengths:
    buy_patterns, last_buy_pattern, last_buy_winrate, last_buy_frequency = find_patterns(ohlc_data, pattern_length, 'up')
    sell_patterns, last_sell_pattern, last_sell_winrate, last_sell_frequency = find_patterns(ohlc_data, pattern_length, 'down')
    all_buy_patterns[pattern_length] = buy_patterns
    all_sell_patterns[pattern_length] = sell_patterns

Formasyon arama filtremizi 3 ila 25 arasındaki her uzunluk için başlatıyoruz. Neden bu şekilde yapıyoruz? Üç fiyat hareketinden daha az olan formasyonlar çok güvensizdir - bundan daha önce bahsetmiştik. 25'ten uzun formasyonlar çok nadirdir. Her uzunluk için hem alış hem de satış formasyonlarına bakıyoruz. 

Ama neden bu kadar çok farklı uzunluğa ihtiyacımız var? Kısa formasyonlar hızlı piyasa dönüşlerini yakalayabilirken, uzun formasyonlar uzun vadeli trendleri gösterebilir. Neyin daha etkili olacağını önceden bilmiyoruz, bu yüzden her şeyi test ediyoruz.


Alış ve satış formasyonu analizi

Artık farklı uzunluklarda bir formasyon seçkimiz olduğuna göre, hangilerinin gerçekten çalıştığını belirlemenin zamanı geldi. 

Kodumuza göz atalım:

all_buy_results = []
for pattern_length, patterns in all_buy_patterns.items():
    results = calculate_winrate_and_frequency(patterns)
    all_buy_results.extend(results)

all_sell_results = []
for pattern_length, patterns in all_sell_patterns.items():
    results = calculate_winrate_and_frequency(patterns)
    all_sell_results.extend(results)

Hem alış hem de satış olmak üzere her formasyonu kazanç oranı ve sıklık hesaplayıcımız aracılığıyla sıralıyoruz. 

Ancak biz sadece istatistikleri saymıyoruz. Alış ve satış formasyonları arasındaki farka bakıyoruz. Bu neden önemli? Çünkü piyasa yükselirken ve düşerken farklı davranabilir. Bazen alış formasyonları daha iyi sonuç verirken bazen de satış formasyonları daha güvenilir hale gelir.

Ardından bir sonraki adıma geçeceğiz - farklı uzunluklardaki formasyonları birbirleriyle karşılaştıracağız. Kısa formasyonların piyasaya giriş noktasını belirlemek için, uzun formasyonların ise uzun vadeli trendi belirlemek için daha iyi çalıştığı ortaya çıkabilir. Bunun tam tersi de olabilir. Bu yüzden her şeyi analiz ediyor ve hiçbir şeyi önceden atmıyoruz.

Bu analizin sonunda, ilk sonuçları oluşturuyoruz: hangi formasyonlar alış için, hangileri satış için daha iyi çalışıyor, farklı piyasa koşullarında hangi uzunluktaki formasyonlar en etkili oluyor. Bu verilerle, Forex piyasasındaki fiyatların bir analizini zaten yapabiliriz.

Ancak unutmayın ki en iyi formasyon bile başarının garantisi değildir. Piyasa sürprizlerle doludur. Bizim görevimiz başarı şansını artırmaktır ve bunu da formasyonları her yönden analiz ederek yapıyoruz.


İleriye bakış: Son formasyonlara dayalı tahmin

Şimdi sıra tahminler yapmaya geldi. Tahmin kodumuza bir göz atalım:

if last_buy_pattern:
    print(f"\nLast buy pattern for {symbol}: {last_buy_pattern}, Winrate: {last_buy_winrate:.2f}%, Frequency: {last_buy_frequency}")
    print(f"Forecast: Price will likely go up.")
if last_sell_pattern:
    print(f"\nLast sell pattern for {symbol}: {last_sell_pattern}, Winrate: {last_sell_winrate:.2f}%, Frequency: {last_sell_frequency}")
    print(f"Forecast: Price will likely go down.")

Oluşan son formasyona bakıyoruz ve geleceği tahmin etmeye ve alım-satım analizimizi yapmaya çalışıyoruz.

Lütfen iki senaryoyu göz önünde bulundurduğumuzu unutmayın: bir alış formasyonu ve bir satış formasyonu. Neden? Çünkü piyasa boğalar ve ayılar, yani alıcılar ve satıcılar arasında ebedi bir çatışmadır. Her türlü duruma karşı hazırlıklı olmalıyız.

Her bir formasyon için şu üç temel parametrenin çıktısını alıyoruz: formasyonun kendisi, kazanç oranı ve oluşma sıklığı. Kazanç oranı özellikle önemlidir. Bir alış formasyonunun %70 kazanç oranına sahip olması, bu formasyonun ortaya çıkmasından sonra %70 olasılıkla fiyatın gerçekten yükseldiği anlamına gelir. Bu oldukça iyi bir sonuçtur. Ancak unutmayın, %90 bile bir garanti değildir. Forex dünyasında sürprizlere her zaman yer vardır.

Sıklık da önemli bir rol oynar. Sık görülen bir formasyon, nadir görülen bir formasyondan daha güvenilirdir.

Asıl ilginç olan kısım ise tahminlerimiz. "Price will likely go up" veya "Price will likely go down". Bu tahminler, yaptığımız çalışmadan bir miktar memnuniyet duymamızı sağlıyor. Ancak unutmayın ki en doğru tahmin bile sadece bir olasılıktır, garanti değildir. Forex piyasasını tahmin etmek oldukça zordur. Haberler, ekonomik olaylar, hatta nüfuz sahibi kişilerin tweet'leri bile fiyat hareketinin yönünü birkaç saniye içinde değiştirebilir.

Bu nedenle, kodumuz her derde deva değil, daha ziyade çok akıllı bir Uzman Danışmandır. Şu şekilde yorumlanabilir: "Bakın, geçmiş verilere dayanarak fiyatın yükseleceğine (veya düşeceğine) inanmak için nedenlerimiz var". Piyasaya girip girmemeye karar vermek size kalmıştır. Bu tahminleri uygulamak, üzerinde düşünülmesi gereken bir süreçtir. Olası hareketler hakkında bilgi edindiniz, ancak yine de her adımın piyasadaki genel durum dikkate alınarak akıllıca atılması gerekiyor.


Geleceği çizme: En iyi formasyonların ve tahminlerin görselleştirilmesi

Kodumuza biraz görselleştirme sihri ekleyelim:

import matplotlib.pyplot as plt

def visualize_patterns(patterns, title, filename):
    patterns = patterns[:20]  # Take top 20 for clarity
    patterns.reverse()  # Reverse the list to display it correctly on the chart

    fig, ax = plt.subplots(figsize=(12, 8))
    
    winrates = [p[1] for p in patterns]
    frequencies = [p[2] for p in patterns]
    labels = [' '.join(p[0]) for p in patterns]

    ax.barh(range(len(patterns)), winrates, align='center', color='skyblue', zorder=10)
    ax.set_yticks(range(len(patterns)))
    ax.set_yticklabels(labels)
    ax.invert_yaxis()  # Invert the Y axis to display the best patterns on top

    ax.set_xlabel('Winrate (%)')
    ax.set_title(title)

    # Add occurrence frequency
    for i, v in enumerate(winrates):
        ax.text(v + 1, i, f'Freq: {frequencies[i]}', va='center')

    plt.tight_layout()
    plt.savefig(filename)
    plt.close()

# Visualize top buy and sell patterns
visualize_patterns(top_300_buy_patterns, f'Top 20 Buy Patterns for {symbol}', 'top_buy_patterns.png')
visualize_patterns(top_300_sell_patterns, f'Top 20 Sell Patterns for {symbol}', 'top_sell_patterns.png')

# Visualize the latest pattern and forecast
def visualize_forecast(pattern, winrate, frequency, direction, symbol, filename):
    fig, ax = plt.subplots(figsize=(8, 6))
    
    ax.bar(['Winrate'], [winrate], color='green' if direction == 'up' else 'red')
    ax.set_ylim(0, 100)
    ax.set_ylabel('Winrate (%)')
    ax.set_title(f'Forecast for {symbol}: Price will likely go {direction}')

    ax.text(0, winrate + 5, f'Pattern: {" ".join(pattern)}', ha='center')
    ax.text(0, winrate - 5, f'Frequency: {frequency}', ha='center')

    plt.tight_layout()
    plt.savefig(filename)
    plt.close()

if last_buy_pattern:
    visualize_forecast(last_buy_pattern, last_buy_winrate, last_buy_frequency, 'up', symbol, 'buy_forecast.png')
if last_sell_pattern:
    visualize_forecast(last_sell_pattern, last_sell_winrate, last_sell_frequency, 'down', symbol, 'sell_forecast.png')

İki fonksiyon oluşturduk: visualize_patterns ve visualize_forecast. İlki, en iyi 20 formasyonu, bunların kazanç oranlarını ve oluşma sıklıklarını içeren bilgilendirici bir yatay çubuk grafiği çizer. İkincisi, en son formasyona dayalı olarak tahminimizin görsel bir temsilini oluşturur.

Formasyonlar için yatay sütunlar kullanıyoruz çünkü formasyonlar uzun olabiliyor ve bu da okunmalarını kolaylaştırıyor. İnsan gözünün algılaması için hoş bir renge sahibiz - gök mavisi.

Bu görselleri PNG dosyalarına kaydediyoruz.


Formasyon analiz sisteminin geriye dönük test edilmesi

Formasyon analiz sistemimizi oluşturduk, ancak gerçekten işe yarayıp yaramadığını nasıl bileceğiz? Bunu yapmak için geçmiş veriler üzerinde test etmemiz gerekir. 

İşte bu görev için gereken kodumuz:

def simulate_trade(data, direction, entry_price, take_profit, stop_loss):
    for i, row in data.iterrows():
        current_price = row['close']
        
        if direction == "BUY":
            if current_price >= entry_price + take_profit:
                return {'profit': take_profit, 'duration': i}
            elif current_price <= entry_price - stop_loss:
                return {'profit': -stop_loss, 'duration': i}
        else:  # SELL
            if current_price <= entry_price - take_profit:
                return {'profit': take_profit, 'duration': i}
            elif current_price >= entry_price + stop_loss:
                return {'profit': -stop_loss, 'duration': i}
    
    # If the loop ends without reaching TP or SL, close at the current price
    last_price = data['close'].iloc[-1]
    profit = (last_price - entry_price) if direction == "BUY" else (entry_price - last_price)
    return {'profit': profit, 'duration': len(data)}

def backtest_pattern_system(data, buy_patterns, sell_patterns):
    equity_curve = [10000]  # Initial capital $10,000
    trades = []
    
    for i in range(len(data) - max(len(p[0]) for p in buy_patterns + sell_patterns)):
        current_data = data.iloc[:i+1]
        last_pattern = tuple(current_data['direction'].iloc[-len(buy_patterns[0][0]):])
        
        matching_buy = [p for p in buy_patterns if p[0] == last_pattern]
        matching_sell = [p for p in sell_patterns if p[0] == last_pattern]
        
        if matching_buy and not matching_sell:
            entry_price = current_data['close'].iloc[-1]
            take_profit = 0.001  # 10 pips
            stop_loss = 0.0005  # 5 pips
            trade_result = simulate_trade(data.iloc[i+1:], "BUY", entry_price, take_profit, stop_loss)
            trades.append(trade_result)
            equity_curve.append(equity_curve[-1] + trade_result['profit'] * 10000)  # Multiply by 10000 to convert to USD
        elif matching_sell and not matching_buy:
            entry_price = current_data['close'].iloc[-1]
            take_profit = 0.001  # 10 pips
            stop_loss = 0.0005  # 5 pips
            trade_result = simulate_trade(data.iloc[i+1:], "SELL", entry_price, take_profit, stop_loss)
            trades.append(trade_result)
            equity_curve.append(equity_curve[-1] + trade_result['profit'] * 10000)  # Multiply by 10000 to convert to USD
        else:
            equity_curve.append(equity_curve[-1])
    
    return equity_curve, trades

# Conduct a backtest
equity_curve, trades = backtest_pattern_system(ohlc_data, top_300_buy_patterns, top_300_sell_patterns)

# Visualizing backtest results
plt.figure(figsize=(12, 6))
plt.plot(equity_curve)
plt.title('Equity Curve')
plt.xlabel('Trades')
plt.ylabel('Equity ($)')
plt.savefig('equity_curve.png')
plt.close()

# Calculating backtest statistics
total_profit = equity_curve[-1] - equity_curve[0]
win_rate = sum(1 for trade in trades if trade['profit'] > 0) / len(trades) if trades else 0
average_profit = sum(trade['profit'] for trade in trades) / len(trades) if trades else 0

print(f"\nBacktest Results:")
print(f"Total Profit: ${total_profit:.2f}")
print(f"Win Rate: {win_rate:.2%}")
print(f"Average Profit per Trade: ${average_profit*10000:.2f}")
print(f"Total Trades: {len(trades)}")

Burada neler oluyor? simulate_trade fonksiyonu, tek bir işlem için simülatörümüzdür. Fiyatı izler ve Kar Al veya Zararı Durdur seviyesine ulaşıldığında işlemi kapatır. 

backtest_pattern_system daha önemli bir fonksiyondur. Geçmiş verilerin üzerinden adım adım, gün gün geçerek formasyonlarımızdan birinin oluşup oluşmadığını kontrol eder. Bir alış formasyonu mu bulundu? O zaman alış yaparız. Bir satış formasyonu mu bulundu? O zaman satış yaparız.

Sabit 100 puan Kar Al ve 50 puan Zararı Durdur kullanıyoruz. Tatmin edici bir kar için sınırları belirlememiz gerekir - gereğinden fazla risk almamak için çok fazla değil, karın artabilmesi için de çok az değil.

Her işlemden sonra varlık eğrimizi güncelliyoruz. Çalışmamızın sonunda şu sonucu elde ediyoruz: toplamda ne kadar kazandık, işlemlerin yüzde kaçı karlıydı, işlem başına ortalama kar ne kadardı? Ve tabii ki sonuçları görselleştiriyoruz.

MQL5 dilini kullanarak formasyon aramayı uygulayalım. İşte kodumuz:

//+------------------------------------------------------------------+
//|                                       PatternProbabilityIndicator|
//|                                                 Copyright 2024   |
//|                                             https://www.mql5.com |
//+------------------------------------------------------------------+
#property copyright "Copyright 2024, Your Name Here"
#property link      "https://www.mql5.com"
#property version   "1.06"
#property indicator_chart_window
#property indicator_buffers 2
#property indicator_plots   2

//--- plot BuyProbability
#property indicator_label1  "BuyProbability"
#property indicator_type1   DRAW_LINE
#property indicator_color1  clrGreen
#property indicator_style1  STYLE_SOLID
#property indicator_width1  2

//--- plot SellProbability
#property indicator_label2  "SellProbability"
#property indicator_type2   DRAW_LINE
#property indicator_color2  clrRed
#property indicator_style2  STYLE_SOLID
#property indicator_width2  2

//--- input parameters
input int      InpPatternLength = 5;    // Pattern Length (3-10)
input int      InpLookback     = 1000;  // Lookback Period (100-5000)
input int      InpForecastHorizon = 6;  // Forecast Horizon (1-20)

//--- indicator buffers
double         BuyProbabilityBuffer[];
double         SellProbabilityBuffer[];

//--- global variables
int            g_pattern_length;
int            g_lookback;
int            g_forecast_horizon;
string         g_patterns[];
int            g_pattern_count;
int            g_pattern_occurrences[];
int            g_pattern_successes[];
int            g_total_bars;

//+------------------------------------------------------------------+
//| Custom indicator initialization function                         |
//+------------------------------------------------------------------+
int OnInit()
{
   //--- validate inputs
   if(InpPatternLength < 3 || InpPatternLength > 10)
   {
      Print("Invalid Pattern Length. Must be between 3 and 10.");
      return INIT_PARAMETERS_INCORRECT;
   }
   
   if(InpLookback < 100 || InpLookback > 5000)
   {
      Print("Invalid Lookback Period. Must be between 100 and 5000.");
      return INIT_PARAMETERS_INCORRECT;
   }

   if(InpForecastHorizon < 1 || InpForecastHorizon > 20)
   {
      Print("Invalid Forecast Horizon. Must be between 1 and 20.");
      return INIT_PARAMETERS_INCORRECT;
   }

   //--- indicator buffers mapping
   SetIndexBuffer(0, BuyProbabilityBuffer, INDICATOR_DATA);
   SetIndexBuffer(1, SellProbabilityBuffer, INDICATOR_DATA);
   
   //--- set accuracy
   IndicatorSetInteger(INDICATOR_DIGITS, 2);
   
   //--- set global variables
   g_pattern_length = InpPatternLength;
   g_lookback = InpLookback;
   g_forecast_horizon = InpForecastHorizon;
   
   //--- generate all possible patterns
   if(!GeneratePatterns())
   {
      Print("Failed to generate patterns.");
      return INIT_FAILED;
   }
   
   g_total_bars = iBars(_Symbol, PERIOD_CURRENT);
   
   return(INIT_SUCCEEDED);
}

//+------------------------------------------------------------------+
//| Custom 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 for rates total
   if(rates_total <= g_lookback + g_pattern_length + g_forecast_horizon)
   {
      Print("Not enough data for calculation.");
      return 0;
   }

   int start = (prev_calculated > g_lookback + g_pattern_length + g_forecast_horizon) ? 
               prev_calculated - 1 : g_lookback + g_pattern_length + g_forecast_horizon;
   
   if(ArraySize(g_pattern_occurrences) != g_pattern_count)
   {
      ArrayResize(g_pattern_occurrences, g_pattern_count);
      ArrayResize(g_pattern_successes, g_pattern_count);
   }
   
   ArrayInitialize(g_pattern_occurrences, 0);
   ArrayInitialize(g_pattern_successes, 0);
   
   // Pre-calculate patterns for efficiency
   string patterns[];
   ArrayResize(patterns, rates_total);
   for(int i = g_pattern_length; i < rates_total; i++)
   {
      patterns[i] = "";
      for(int j = 0; j < g_pattern_length; j++)
      {
         patterns[i] += (close[i-j] > close[i-j-1]) ? "U" : "D";
      }
   }
   
   // Main calculation loop
   for(int i = start; i < rates_total; i++)
   {
      string current_pattern = patterns[i];
      
      if(StringLen(current_pattern) != g_pattern_length) continue;
      
      double buy_probability = CalculateProbability(current_pattern, true, close, patterns, i);
      double sell_probability = CalculateProbability(current_pattern, false, close, patterns, i);
      
      BuyProbabilityBuffer[i] = buy_probability;
      SellProbabilityBuffer[i] = sell_probability;
   }
   
   // Update Comment with pattern statistics if total bars changed
   if(g_total_bars != iBars(_Symbol, PERIOD_CURRENT))
   {
      g_total_bars = iBars(_Symbol, PERIOD_CURRENT);
      UpdatePatternStatistics();
   }
   
   return(rates_total);
}

//+------------------------------------------------------------------+
//| Generate all possible patterns                                   |
//+------------------------------------------------------------------+
bool GeneratePatterns()
{
   g_pattern_count = (int)MathPow(2, g_pattern_length);
   if(!ArrayResize(g_patterns, g_pattern_count))
   {
      Print("Failed to resize g_patterns array.");
      return false;
   }
   
   for(int i = 0; i < g_pattern_count; i++)
   {
      string pattern = "";
      for(int j = 0; j < g_pattern_length; j++)
      {
         pattern += ((i >> j) & 1) ? "U" : "D";
      }
      g_patterns[i] = pattern;
   }
   
   return true;
}

//+------------------------------------------------------------------+
//| Calculate probability for a given pattern                        |
//+------------------------------------------------------------------+
double CalculateProbability(const string &pattern, bool is_buy, const double &close[], const string &patterns[], int current_index)
{
   if(StringLen(pattern) != g_pattern_length || current_index < g_lookback)
   {
      return 50.0; // Return neutral probability on error
   }

   int pattern_index = ArraySearch(g_patterns, pattern);
   if(pattern_index == -1)
   {
      return 50.0;
   }

   int total_occurrences = 0;
   int successful_predictions = 0;
   
   for(int i = g_lookback; i > g_pattern_length + g_forecast_horizon; i--)
   {
      int historical_index = current_index - i;
      if(historical_index < 0 || historical_index + g_pattern_length + g_forecast_horizon >= ArraySize(close))
      {
         continue;
      }

      if(patterns[historical_index] == pattern)
      {
         total_occurrences++;
         g_pattern_occurrences[pattern_index]++;
         if(is_buy && close[historical_index + g_pattern_length + g_forecast_horizon] > close[historical_index + g_pattern_length])
         {
            successful_predictions++;
            g_pattern_successes[pattern_index]++;
         }
         else if(!is_buy && close[historical_index + g_pattern_length + g_forecast_horizon] < close[historical_index + g_pattern_length])
         {
            successful_predictions++;
            g_pattern_successes[pattern_index]++;
         }
      }
   }
   
   return (total_occurrences > 0) ? (double)successful_predictions / total_occurrences * 100 : 50;
}

//+------------------------------------------------------------------+
//| Update pattern statistics and display in Comment                 |
//+------------------------------------------------------------------+
void UpdatePatternStatistics()
{
   string comment = "Pattern Statistics:\n";
   comment += "Pattern Length: " + IntegerToString(g_pattern_length) + "\n";
   comment += "Lookback Period: " + IntegerToString(g_lookback) + "\n";
   comment += "Forecast Horizon: " + IntegerToString(g_forecast_horizon) + "\n\n";
   comment += "Top 5 Patterns:\n";
   
   int sorted_indices[];
   ArrayResize(sorted_indices, g_pattern_count);
   for(int i = 0; i < g_pattern_count; i++) sorted_indices[i] = i;
   
   // Use quick sort for better performance
   ArraySort(sorted_indices);
   
   for(int i = 0; i < 5 && i < g_pattern_count; i++)
   {
      int idx = sorted_indices[g_pattern_count - 1 - i];  // Reverse order for descending sort
      double win_rate = g_pattern_occurrences[idx] > 0 ? 
                        (double)g_pattern_successes[idx] / g_pattern_occurrences[idx] * 100 : 0;
      
      comment += g_patterns[idx] + ": " +
                 "Occurrences: " + IntegerToString(g_pattern_occurrences[idx]) + ", " +
                 "Win Rate: " + DoubleToString(win_rate, 2) + "%\n";
   }
   
   Comment(comment);
}

//+------------------------------------------------------------------+
//| Custom function to search for a string in an array               |
//+------------------------------------------------------------------+
int ArraySearch(const string &arr[], string value)
{
   for(int i = 0; i < ArraySize(arr); i++)
   {
      if(arr[i] == value) return i;
   }
   return -1;
}

Grafikte bu şekilde görünüyor:


Formasyon algılama ve alım-satım için bir Uzman Danışman oluşturma

Sonrasında, Python'da testler başarılı olduğu için MetaTrader 5 sınayıcıda çalışmalarımızı kontrol ettim. Aşağıdaki kod da makaleye eklenmiştir. Kod, Forex piyasasında formasyon analizi kavramının pratik bir uygulamasıdır. Geçmiş fiyat formasyonlarının gelecekteki piyasa hareketleri hakkında istatistiksel olarak anlamlı bilgiler sağlayabileceği fikrini somutlaştırır.

Uzman Danışmanın temel bileşenleri:

  • Formasyon oluşturma: Uzman Danışman, fiyat hareketlerinin ikili bir gösterimini (yükseliş veya düşüş) kullanır ve belirli bir formasyon uzunluğu için tüm olası kombinasyonları oluşturur.
  • İstatistiksel analiz: Uzman Danışman, geriye dönük bir analiz gerçekleştirerek her bir formasyonun ortaya çıkma sıklığını ve tahmin etkinliğini değerlendirir.
  • Dinamik adaptasyon: Uzman Danışman, değişen piyasa koşullarına uyum sağlamak için formasyon istatistiklerini sürekli olarak günceller.
  • Alım-satım kararları verme: Uzman Danışman, alış ve satış için belirlenen en etkili formasyonlara dayanarak pozisyonları açar, kapatır veya tutar.
  • Parametrelendirme: Uzman Danışman, formasyon uzunluğu, analiz dönemi, tahmin ufku ve minimum formasyon oluşumu sayısı gibi temel parametreleri özelleştirme olanağı sağlar.

Toplamda, Uzman Danışmanın 4 versiyonunu yaptım: ilki makalenin konseptine dayanıyor, formasyonlara dayalı işlemler açıyor ve ters yönde yeni bir daha iyi formasyon tespit edildiğinde bunları kapatıyor. İkincisi aynıdır, ancak çok dövizlidir: Dünya Bankası istatistiklerine göre en likit 10 Forex paritesi ile çalışıyor. Üçüncüsü de aynıdır, ancak fiyat tahmin ufkundan daha fazla sayıda çubuğu geçtiğinde işlemleri kapatıyor. Sonuncusu Kar Al ve Zararı Durdur ile işlemleri kapatıyor.

İşte ilk Uzman Danışmanın kodu, geri kalanı ekli dosyalardadır:

//+------------------------------------------------------------------+
//|                                  PatternProbabilityExpertAdvisor |
//|                                Copyright 2024, Evgeniy Koshtenko |
//|                          https://www.mql5.com/en/users/koshtenko |
//+------------------------------------------------------------------+
#property copyright "Copyright 2023, Evgeniy Koshtenko"
#property link      "https://www.mql5.com/en/users/koshtenko"
#property version   "1.00"

#include <Trade\Trade.mqh>            // Include the CTrade trading class

//--- input parameters
input int      InpPatternLength = 5;    // Pattern Length (3-10)
input int      InpLookback     = 1000;  // Lookback Period (100-5000)
input int      InpForecastHorizon = 6;  // Forecast Horizon (1-20)
input double   InpLotSize = 0.1;        // Lot Size
input int      InpMinOccurrences = 30;  // Minimum Pattern Occurrences

//--- global variables
int            g_pattern_length;
int            g_lookback;
int            g_forecast_horizon;
string         g_patterns[];
int            g_pattern_count;
int            g_pattern_occurrences[];
int            g_pattern_successes[];
int            g_total_bars;
string         g_best_buy_pattern = "";
string         g_best_sell_pattern = "";

CTrade trade;                         // Use the CTrade trading class
//+------------------------------------------------------------------+
//| Expert initialization function                                   |
//+------------------------------------------------------------------+
int OnInit()
{
   //--- validate inputs
   if(InpPatternLength < 3 || InpPatternLength > 10)
   {
      Print("Invalid Pattern Length. Must be between 3 and 10.");
      return INIT_PARAMETERS_INCORRECT;
   }
   
   if(InpLookback < 100 || InpLookback > 5000)
   {
      Print("Invalid Lookback Period. Must be between 100 and 5000.");
      return INIT_PARAMETERS_INCORRECT;
   }

   if(InpForecastHorizon < 1 || InpForecastHorizon > 20)
   {
      Print("Invalid Forecast Horizon. Must be between 1 and 20.");
      return INIT_PARAMETERS_INCORRECT;
   }

   //--- set global variables
   g_pattern_length = InpPatternLength;
   g_lookback = InpLookback;
   g_forecast_horizon = InpForecastHorizon;
   
   //--- generate all possible patterns
   if(!GeneratePatterns())
   {
      Print("Failed to generate patterns.");
      return INIT_FAILED;
   }
   
   g_total_bars = iBars(_Symbol, PERIOD_CURRENT);
   
   return(INIT_SUCCEEDED);
}

//+------------------------------------------------------------------+
//| Expert tick function                                             |
//+------------------------------------------------------------------+
void OnTick()
{
   if(!IsNewBar()) return;
   
   UpdatePatternStatistics();
   
   string current_pattern = GetCurrentPattern();
   
   if(current_pattern == g_best_buy_pattern)
   {
      if(PositionSelect(_Symbol) && PositionGetInteger(POSITION_TYPE) == POSITION_TYPE_SELL)
      {
         trade.PositionClose(_Symbol);
      }
      if(!PositionSelect(_Symbol))
      {
         trade.Buy(InpLotSize, _Symbol, 0, 0, 0, "Buy Pattern: " + current_pattern);
      }
   }
   else if(current_pattern == g_best_sell_pattern)
   {
      if(PositionSelect(_Symbol) && PositionGetInteger(POSITION_TYPE) == POSITION_TYPE_BUY)
      {
         trade.PositionClose(_Symbol);
      }
      if(!PositionSelect(_Symbol))
      {
         trade.Sell(InpLotSize, _Symbol, 0, 0, 0, "Sell Pattern: " + current_pattern);
      }
   }
}

//+------------------------------------------------------------------+
//| Generate all possible patterns                                   |
//+------------------------------------------------------------------+
bool GeneratePatterns()
{
   g_pattern_count = (int)MathPow(2, g_pattern_length);
   if(!ArrayResize(g_patterns, g_pattern_count))
   {
      Print("Failed to resize g_patterns array.");
      return false;
   }
   
   for(int i = 0; i < g_pattern_count; i++)
   {
      string pattern = "";
      for(int j = 0; j < g_pattern_length; j++)
      {
         pattern += ((i >> j) & 1) ? "U" : "D";
      }
      g_patterns[i] = pattern;
   }
   
   return true;
}

//+------------------------------------------------------------------+
//| Update pattern statistics and find best patterns                 |
//+------------------------------------------------------------------+
void UpdatePatternStatistics()
{
   if(ArraySize(g_pattern_occurrences) != g_pattern_count)
   {
      ArrayResize(g_pattern_occurrences, g_pattern_count);
      ArrayResize(g_pattern_successes, g_pattern_count);
   }
   
   ArrayInitialize(g_pattern_occurrences, 0);
   ArrayInitialize(g_pattern_successes, 0);
   
   int total_bars = iBars(_Symbol, PERIOD_CURRENT);
   int start = total_bars - g_lookback;
   if(start < g_pattern_length + g_forecast_horizon) start = g_pattern_length + g_forecast_horizon;
   
   double close[];
   ArraySetAsSeries(close, true);
   CopyClose(_Symbol, PERIOD_CURRENT, 0, total_bars, close);
   
   string patterns[];
   ArrayResize(patterns, total_bars);
   ArraySetAsSeries(patterns, true);
   
   for(int i = 0; i < total_bars - g_pattern_length; i++)
   {
      patterns[i] = "";
      for(int j = 0; j < g_pattern_length; j++)
      {
         patterns[i] += (close[i+j] > close[i+j+1]) ? "U" : "D";
      }
   }
   
   for(int i = start; i >= g_pattern_length + g_forecast_horizon; i--)
   {
      string current_pattern = patterns[i];
      int pattern_index = ArraySearch(g_patterns, current_pattern);
      
      if(pattern_index != -1)
      {
         g_pattern_occurrences[pattern_index]++;
         if(close[i-g_forecast_horizon] > close[i])
         {
            g_pattern_successes[pattern_index]++;
         }
      }
   }
   
   double best_buy_win_rate = 0;
   double best_sell_win_rate = 0;
   
   for(int i = 0; i < g_pattern_count; i++)
   {
      if(g_pattern_occurrences[i] >= InpMinOccurrences)
      {
         double win_rate = (double)g_pattern_successes[i] / g_pattern_occurrences[i];
         if(win_rate > best_buy_win_rate)
         {
            best_buy_win_rate = win_rate;
            g_best_buy_pattern = g_patterns[i];
         }
         if((1 - win_rate) > best_sell_win_rate)
         {
            best_sell_win_rate = 1 - win_rate;
            g_best_sell_pattern = g_patterns[i];
         }
      }
   }
   
   Print("Best Buy Pattern: ", g_best_buy_pattern, " (Win Rate: ", DoubleToString(best_buy_win_rate * 100, 2), "%)");
   Print("Best Sell Pattern: ", g_best_sell_pattern, " (Win Rate: ", DoubleToString(best_sell_win_rate * 100, 2), "%)");
}

//+------------------------------------------------------------------+
//| Get current price pattern                                        |
//+------------------------------------------------------------------+
string GetCurrentPattern()
{
   double close[];
   ArraySetAsSeries(close, true);
   CopyClose(_Symbol, PERIOD_CURRENT, 0, g_pattern_length + 1, close);
   
   string pattern = "";
   for(int i = 0; i < g_pattern_length; i++)
   {
      pattern += (close[i] > close[i+1]) ? "U" : "D";
   }
   
   return pattern;
}

//+------------------------------------------------------------------+
//| Custom function to search for a string in an array               |
//+------------------------------------------------------------------+
int ArraySearch(const string &arr[], string value)
{
   for(int i = 0; i < ArraySize(arr); i++)
   {
      if(arr[i] == value) return i;
   }
   return -1;
}

//+------------------------------------------------------------------+
//| Check if it's a new bar                                          |
//+------------------------------------------------------------------+
bool IsNewBar()
{
   static datetime last_time = 0;
   datetime current_time = iTime(_Symbol, PERIOD_CURRENT, 0);
   if(current_time != last_time)
   {
      last_time = current_time;
      return true;
   }
   return false;
}

Test sonuçlarına gelince, EURUSD üzerinde aşağıdaki gibidir:

Ve ayrıntılı olarak:

Fena değil ve grafik de güzel. Diğer Uzman Danışman versiyonları ya sıfır civarında takılıyor ya da uzun düşüşlere giriyor. Ancak en iyi seçenek benim kriterlerime tam olarak uymuyor. Kar faktörü 2'nin üzerinde ve Sharpe oranı 1'in üzerinde olan Uzman Danışmanları tercih ediyorum. Python sınayıcısında hem işlem komisyonunu hem de makas ve swap'ı hesaba katmak gerektiği aklıma geldi.


Potansiyel iyileştirmeler: Zaman dilimlerini genişletme ve göstergeler ekleme

Düşüncelerimize devam edelim. Sistem kesinlikle olumlu sonuçlar veriyor, ancak bunlar nasıl iyileştirilebilir ve gerçekçi mi?

Şu anda 4 saatlik zaman dilimine bakıyoruz. Daha ileriye bakmaya çalışalım. Günlük, haftalık, hatta belki aylık grafikler de eklemeliyiz. Bu yaklaşım sayesinde daha global trendleri ve daha büyük ölçekli formasyonları görebileceğiz. Kodu tüm bu zaman ölçeklerini kapsayacak şekilde genişletelim:

timeframes = [mt5.TIMEFRAME_H4, mt5.TIMEFRAME_D1, mt5.TIMEFRAME_W1, mt5.TIMEFRAME_MN1]
for tf in timeframes:
    ohlc_data = get_ohlc_data(symbol, tf, start_date, end_date)
    patterns = find_patterns(ohlc_data)

Daha fazla veri, daha fazla gürültü. Daha net veriler elde etmek için bu gürültüyü ayıklamayı öğrenmemiz gerekiyor.

Analiz edilen özelliklerin kapsamını genişletelim. Alım-satım dünyasında bu, teknik göstergelerin eklenmesidir. RSI, MACD ve Bollinger Bands en sık kullanılan araçlardır.

def add_indicators(data):
    data['RSI'] = ta.RSI(data['close'])
    data['MACD'] = ta.MACD(data['close']).macd()
    data['BB_upper'], data['BB_middle'], data['BB_lower'] = ta.BBANDS(data['close'])
    return data

ohlc_data = add_indicators(ohlc_data)

Göstergeler, formasyon sinyallerimizi doğrulamamıza yardımcı olabilir. Ya da ek olarak göstergeler üzerinde formasyonlar arayabiliriz.


Sonuç

Böylece formasyonları bulma ve analiz etme çalışmalarımızı tamamladık. Piyasa kaosunda formasyonları arayan bir sistem oluşturduk. Sonuçlarımızı görselleştirmeyi, geriye dönük testler gerçekleştirmeyi ve gelecekteki iyileştirmeler için plan yapmayı öğrendik. Ama en önemlisi, analitik yatırımcılar gibi düşünmeyi öğrendik. Sadece kalabalığı takip etmiyoruz, kendi yolumuzu, kendi formasyonlarımızı, kendi olanaklarımızı arıyoruz.

Piyasanın, yaşayan insanların eylemlerinin bir ürünü olduğunu unutmayın. Sürekli bir değişim geçirir. Bizim görevimiz de onunla birlikte değişmektir. Bugünün formasyonları yarın işe yaramayabilir, ancak bu umutsuzluğa kapılmak için bir neden değildir. Bu, öğrenmek, uyum sağlamak ve gelişmek için bir fırsattır. Bu sistemi bir başlangıç noktası olarak kullanın. Deneyin, iyileştirin ve kendinizinkini yaratın. Belki de başarılı alım-satımın kapılarını açacak olan o formasyonu bulacaksınız!

Bu heyecan verici yolculukta size iyi şanslar! Formasyonlarınız her zaman karlı olsun ve kayıplar başarıya giden yolda sadece ders olsun. Forex dünyasında yakında görüşmek üzere!

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

Ekli dosyalar |
PredictPattern.py (9.23 KB)
AutoPattern.mq5 (18.98 KB)
PatternEA.mq5 (16.12 KB)
PatternEAMult.mq5 (9.59 KB)
Son yorumlar | Tartışmaya git (1)
linfo2
linfo2 | 9 May 2025 saat 02:09
Teşekkürler Yevgniy , Harika bir şablon python ile bir fikri değerlendiriyor. çok takdir ediyorum
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.
MetaTrader 5'i kullanarak Python'da yüksek frekanslı arbitraj alım-satım sistemi MetaTrader 5'i kullanarak Python'da yüksek frekanslı arbitraj alım-satım sistemi
Bu makalede, Forex piyasasında binlerce sentetik fiyat oluşturan, bunları analiz eden ve kar için başarılı bir şekilde işlem yapan ve aynı zamanda aracı kurumların gözünde yasal olarak kalan bir arbitraj sistemi oluşturacağız.
İş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.
William Gann yöntemleri (Bölüm III): Astroloji işe yarıyor mu? William Gann yöntemleri (Bölüm III): Astroloji işe yarıyor mu?
Gezegenlerin ve yıldızların konumları finansal piyasaları etkiliyor mu? Kendimizi istatistikler ve büyük verilerle donatalım ve yıldızlarla hisse senedi grafiklerinin kesiştiği dünyaya doğru heyecan verici bir yolculuğa çıkalım.