English Русский 中文 Español Deutsch 日本語 Português Italiano
preview
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

MetaTrader 5Alım-satım |
28 7
Yevgeniy Koshtenko
Yevgeniy Koshtenko

Giriş

Forex piyasası. Algoritmik stratejiler. Python ve MetaTrader 5. Bunlar bir arbitraj alım-satım sistemi üzerinde çalışmaya başladığımda bir araya geldi. Fikir basitti - fiyat dengesizliklerini bulmak için yüksek frekanslı bir sistem oluşturmak.

Bu süreçte en çok MetaTrader 5 API’sini kullandım. Sentetik çapraz kurları hesaplamaya karar verdim. Kendimi on ya da yüz taneyle sınırlamadım. Bu sayı bini aşmış durumda.

Risk yönetimi ayrı bir görevdi. Sistem mimarisi, algoritmalar, karar verme - burada her şeyi analiz edeceğiz. Geriye dönük test ve canlı alım-satım sonuçlarını göstereceğim. Ve elbette gelecek için fikirlerimi paylaşacağım. Kim bilir, belki içinizden biri bu konuyu daha da geliştirmek ister? Çalışmalarımın talep göreceğini umuyorum. Algoritmik alım-satımın gelişmesine katkıda bulunacağına inanıyorum. Belki birileri bunu temel alır ve yüksek frekanslı arbitraj dünyasında daha da etkili bir şey yaratır. Ne de olsa bilimin özü budur - öncekilerin deneyimlerine dayanarak ilerlemek. Doğrudan konuya girelim.


Forex arbitraj alım-satımına giriş

Gerçekte ne olduğunu anlayalım.

Döviz değişimi ile bir analoji kurulabilir. Diyelim ki bir yerden EUR karşılığında USD satın alabilir, hemen başka bir yerde bu USD'yi GBP karşılığında satabilir ve ardından GBP'yi tekrar EUR'ya çevirip kar elde edebilirsiniz. Bu en basit haliyle arbitrajdır.

Aslında durum biraz daha karmaşıktır. Forex devasa, merkezi olmayan bir piyasadır. Burada çok sayıda banka, aracı kurum ve fon vardır. Ve herkesin kendi döviz kurları vardır. Çoğu zaman eşleşmezler. İşte bu noktada arbitraj için bir fırsata sahibiz. Ama bunun kolay para olduğunu düşünmeyin. Genellikle bu fiyat farklılıkları sadece birkaç saniye sürer. Hatta milisaniyeler. Zamanında yetişmek neredeyse imkansız. Bunun için güçlü bilgisayarlar ve hızlı algoritmalar gerekir.

Farklı arbitraj türleri vardır. Basit olanı, farklı yerlerdeki kurlar arasındaki farktan kar elde etmektir. Karmaşık olanı ise çapraz kurları kullanmaktır. Örneğin, GBP'nin USD ve EUR cinsinden ne kadara satılacağını hesaplıyor ve bunu doğrudan GBP/EUR döviz kuru ile karşılaştırıyoruz.

Liste bununla da bitmiyor. Ayrıca zaman arbitrajı da vardır. Burada, zamanın farklı noktalarındaki fiyat farklarından kar elde ediyoruz. Şimdi almak, bir dakika içinde satmak. Elbette süreç basit görünüyor. Ancak asıl sorun, fiyatın bir dakika içinde nereye gideceğini bilmememiz. Bunlar ana risklerdir. Piyasa, istediğiniz emri etkinleştirebileceğiniz süreden daha hızlı bir şekilde tersine dönebilir. Ya da aracı kurumunuz emirleri gerçekleştirmeyi geciktirebilir. Genel olarak, oldukça fazla zorluk ve risk vardır. Tüm zorluklara rağmen, Forex arbitrajı oldukça popüler bir sistemdir. Burada ciddi finansal kaynaklar ve yalnızca bu tür alım-satımda uzmanlaşmış yeterli sayıda yatırımcı vardır.

Şimdi, kısa bir girişten sonra, stratejimize geçelim.


Kullanılan teknolojilere genel bakış: Python ve MetaTrader 5

Python ve MetaTrader 5. 

Python çok yönlü ve anlaşılması kolay bir programlama dilidir. Hem acemi hem de deneyimli geliştiriciler tarafından tercih edilmesi boşuna değildir. Ve veri analizi için en uygun olanıdır.

Öte yandan, MetaTrader 5. Bu, her Forex yatırımcısının aşina olduğu bir platformdur. Güvenilirdir ve karmaşık değildir. Ayrıca oldukça işlevseldir - gerçek zamanlı fiyatlar, alım-satım robotları ve teknik analiz. Hepsi bir arada uygulama. Pozitif sonuçlar elde etmek için tüm bunları birleştirmemiz gerekiyor.

Python, MetaTrader 5'ten verileri alır, kütüphanelerini kullanarak işler ve ardından alım-satım işlemlerini gerçekleştirmek için komutları MetaTrader 5'e geri gönderir. Elbette bazı zorluklar var. Ancak bu uygulamalar birlikte çok verimlidir.

Python'dan MetaTrader 5 ile çalışmak için geliştiricilerin özel bir kütüphanesi mevcuttur. Etkinleştirmek için yüklemeniz yeterlidir. Bunu yaptıktan sonra fiyatları alabilir, emirler gönderebilir ve pozisyonları yönetebiliriz. Her şey terminalin kendisinde olduğu gibi, sadece şimdi Python yetenekleri de kullanılıyor.

Şu anda hangi özellik ve kabiliyetlere sahibiz? Artık oldukça fazla sayıda. Örneğin, alım-satımı otomatikleştirebiliyor ve geçmiş verilerin karmaşık analizini yapabiliyoruz. Kendi işlem platformumuzu bile oluşturabiliriz. Bu ileri düzey kullanıcılar için bir görevdir, ancak bu da mümkündür.


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

İş akışımıza Python ile başlayacağız. Eğer henüz yüklemediyseniz, python.org adresine gidin. Ayrıca “Add Python to PATH” seçeneğini de işaretlemeniz gerekiyor.

Bir sonraki adımımız kütüphaneler. Birkaç tanesine ihtiyacımız olacak. Ana olan MetaTrader 5'tir. Kurulum herhangi bir özel beceri gerektirmez.

Komut satırını açın ve şunu yazın:

pip install MetaTrader5 pandas numpy

Enter'a basın ve gidip biraz kahve için. Ya da çay. Ya da neyi tercih ederseniz.

Her şey hazır mı? Şimdi MetaTrader 5'e bağlanma zamanı.

Yapmanız gereken ilk şey MetaTrader 5'in kendisini yüklemektir. Aracı kurumunuzdan indirin. Terminale giden yolu hatırladığınızdan emin olun. Tipik olarak şöyle görünür: "C:\ProgramFiles\MetaTrader 5\terminal64.exe".

Ş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("Alas! Failed to connect :(")
    mt5.shutdown()
else:
    print("Hooray! Connection successful!")

Her şey başladıysa, bir sonraki bölüme geçebilirsiniz.


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

İçe aktarmalarla başlıyoruz: MetaTrader5, pandas, datetime, pytz... Daha sonra, fonksiyonlar var:

  • İlk fonksiyon remove_duplicate_indices'tır. Verilerimizde herhangi bir tekrar olmadığından emin olur.
  • Ardından get_mt5_data gelir. MetaTrader 5 fonksiyonlarına erişir ve son 24 saat için gerekli verileri çıkarır.
  • get_currency_data - çok ilginç bir fonksiyon. Bir grup döviz paritesi için get_mt5_data'yı çağırır. AUDUSD, EURUSD, GBPJPY ve daha birçok parite.
  • Bir sonraki calculate_synthetic_prices’tır. Bu fonksiyon gerçek bir başarıdır. Döviz paritelerini işlerken yüzlerce sentetik fiyat üretir.
  • analyze_arbitrage, gerçek fiyatları sentetik fiyatlarla karşılaştırarak arbitraj fırsatları arar. Tüm bulgular bir CSV dosyasına kaydedilir.
  • open_test_limit_order - başka bir güçlü kod birimi. Bir arbitraj fırsatı bulunduğunda, bu fonksiyon bir test emri açar. Ancak aynı anda 10'dan fazla açık işlem olmamalıdır.

Ve son olarak, 'main' fonksiyonu. Fonksiyonları doğru sırada çağırarak tüm bu süreci yönetir.

Kodun sonunda sonsuz bir döngü vardır. Her 5 dakikada bir tüm döngüyü çalıştırır, ancak yalnızca çalışma saatleri içinde. Sahip olduğumuz yapı budur. Basit ama etkili. 


MetaTrader 5'ten verileri alma: get_mt5_data fonksiyonu

İlk görev terminalden verileri almaktır.

if not mt5.initialize(path=terminal_path):
    print(f"Failed to connect to MetaTrader 5 terminal at {terminal_path}")
    return None
timezone = pytz.timezone("Etc/UTC")
utc_from = datetime.now(timezone) - timedelta(days=1)

UTC kullandığımızı unutmayın. Çünkü Forex dünyasında saat dilimi karmaşasına yer yoktur.

Şimdi en önemli şey tikleri elde etmek:

ticks = mt5.copy_ticks_from(symbol, utc_from, count, mt5.COPY_TICKS_ALL)

Verileri aldınız mı? Harika! Şimdi bu verileri işlememiz gerekiyor. Bunu yapmak için pandas’ı kullanıyoruz:

ticks_frame = pd.DataFrame(ticks)
ticks_frame['time'] = pd.to_datetime(ticks_frame['time'], unit='s')

İşte bu kadar! Artık verileri içeren kendi DataFrame'imiz var. Analiz için çoktan hazır durumda.

Peki ya bir şeyler ters giderse? Merak etmeyin! Fonksiyonumuz bunu da kapsamaktadır:

if ticks is None:
    print(f"Failed to fetch data for {symbol}")
    return None

Basitçe bir sorun bildirecek ve None geri döndürecektir.


Birden fazla döviz paritesini işleme: get_currency_data fonksiyonu

get_currency_data fonksiyonu ile sisteme daha fazla dalıyoruz. Koda bir göz atalım:

def get_currency_data():
    # Define currency pairs and the amount of data
    symbols = ["AUDUSD", "AUDJPY", "CADJPY", "AUDCHF", "AUDNZD", "USDCAD", "USDCHF", "USDJPY", "NZDUSD", "GBPUSD", "EURUSD", "CADCHF", "CHFJPY", "NZDCAD", "NZDCHF", "NZDJPY", "GBPCAD", "GBPCHF", "GBPJPY", "GBPNZD", "EURCAD", "EURCHF", "EURGBP", "EURJPY", "EURNZD"]
    count = 1000  # number of data points for each currency pair
    data = {}
    for symbol in symbols:
        df = get_mt5_data(symbol, count, terminal_path)
        if df is not None:
            data[symbol] = df[['time', 'bid', 'ask']].set_index('time')
    return data

Her şey döviz paritelerini tanımlamakla başlıyor. Liste AUDUSD, EURUSD, GBPJPY ve bizim için iyi bilinen diğer enstrümanları içeriyor.

Ardından bir sonraki adıma geçiyoruz. Fonksiyon boş bir 'data' sözlüğü oluşturur. Daha sonra gerekli verilerle doldurulacaktır.

Şimdi fonksiyonumuz çalışmaya başlıyor. Döviz pariteleri listesinin üzerinden geçecektir. Her parite için get_mt5_data çağrısı yapar. Eğer get_mt5_data veri geri döndürürse (None değil), fonksiyonumuz sadece en önemlilerini alır: zaman, Bid fiyatı ve Ask fiyatı.

Ve işte, nihayet, büyük final. Fonksiyon, verilerle dolu bir sözlük geri döndürür. 

Böylece fonksiyon tamamlanır. Küçük, güçlü, basit ama etkilidir.


2000 sentetik fiyatın hesaplanması: Strateji ve uygulama

Sistemimizin temellerine dalıyoruz - calculate_synthetic_prices fonksiyonu. Sentezlenmiş verilerimizi elde etmemizi sağlar.

Koda bir göz atalım:

def calculate_synthetic_prices(data):
    synthetic_prices = {}

    # Remove duplicate indices from all DataFrames in the data dictionary
    for key in data:
        data[key] = remove_duplicate_indices(data[key])

    # Calculate synthetic prices for all pairs using multiple methods
    pairs = [('AUDUSD', 'USDCHF'), ('AUDUSD', 'NZDUSD'), ('AUDUSD', 'USDJPY'),
             ('USDCHF', 'USDCAD'), ('USDCHF', 'NZDCHF'), ('USDCHF', 'CHFJPY'),
             ('USDJPY', 'USDCAD'), ('USDJPY', 'NZDJPY'), ('USDJPY', 'GBPJPY'),
             ('NZDUSD', 'NZDCAD'), ('NZDUSD', 'NZDCHF'), ('NZDUSD', 'NZDJPY'),
             ('GBPUSD', 'GBPCAD'), ('GBPUSD', 'GBPCHF'), ('GBPUSD', 'GBPJPY'),
             ('EURUSD', 'EURCAD'), ('EURUSD', 'EURCHF'), ('EURUSD', 'EURJPY'),
             ('CADCHF', 'CADJPY'), ('CADCHF', 'GBPCAD'), ('CADCHF', 'EURCAD'),
             ('CHFJPY', 'GBPCHF'), ('CHFJPY', 'EURCHF'), ('CHFJPY', 'NZDCHF'),
             ('NZDCAD', 'NZDJPY'), ('NZDCAD', 'GBPNZD'), ('NZDCAD', 'EURNZD'),
             ('NZDCHF', 'NZDJPY'), ('NZDCHF', 'GBPNZD'), ('NZDCHF', 'EURNZD'),
             ('NZDJPY', 'GBPNZD'), ('NZDJPY', 'EURNZD')]

    method_count = 1
    for pair1, pair2 in pairs:
        print(f"Calculating synthetic price for {pair1} and {pair2} using method {method_count}")
        synthetic_prices[f'{pair1}_{method_count}'] = data[pair1]['bid'] / data[pair2]['ask']
        method_count += 1
        print(f"Calculating synthetic price for {pair1} and {pair2} using method {method_count}")
        synthetic_prices[f'{pair1}_{method_count}'] = data[pair1]['bid'] / data[pair2]['bid']
        method_count += 1

    return pd.DataFrame(synthetic_prices)


Arbitraj fırsatlarını analiz etme: analyze_arbitrage fonksiyonu

İlk olarak, boş bir synthetic_prices sözlüğü oluşturuyoruz. Daha sonra onu verilerle dolduracağız. Ardından tüm verilerin üzerinden geçiyor ve gelecekte hataları önlemek için tekrar eden indeksleri kaldırıyoruz.

Bir sonraki adım 'pairs' listesidir. Bunlar sentez için kullanacağımız döviz paritelerimizdir. Ardından başka bir süreç başlıyor. Tüm pariteler üzerinde bir döngü çalıştırıyoruz. Her bir parite için sentetik fiyatı iki şekilde hesaplıyoruz:

  1. İlk paritenin Bid fiyatını ikinci paritenin Ask fiyatına bölüyoruz.
  2. İlk paritenin Bid fiyatını ikinci paritenin Bid fiyatına bölüyoruz.

Her seferinde method_count değerini artırıyoruz. Sonuç olarak, 2000 sentetik parite elde ediyoruz!

calculate_synthetic_prices fonksiyonu bu şekilde çalışıyor. Sadece fiyatları hesaplamakla kalmaz, aslında yeni fırsatlar yaratır. Bu fonksiyon arbitraj fırsatları şeklinde harika sonuçlar verir!


Sonuçları görselleştirme: Verileri CSV'ye kaydetme

analyze_arbitrage fonksiyonuna bakalım. Sadece verileri analiz etmekle kalmaz, ihtiyaç duyulan şeyi bir sayı akışı içinde arar. Fonksiyona göz atalım:

def analyze_arbitrage(data, synthetic_prices, method_count):
    # Calculate spreads for each pair
    spreads = {}
    for pair in data.keys():
        for i in range(1, method_count + 1):
            synthetic_pair = f'{pair}_{i}'
            if synthetic_pair in synthetic_prices.columns:
                print(f"Analyzing arbitrage opportunity for {synthetic_pair}")
                spreads[synthetic_pair] = data[pair]['bid'] - synthetic_prices[synthetic_pair]
    # Identify arbitrage opportunities
    arbitrage_opportunities = pd.DataFrame(spreads) > 0.00008
    print("Arbitrage opportunities:")
    print(arbitrage_opportunities)
    # Save the full table of arbitrage opportunities to a CSV file
    arbitrage_opportunities.to_csv('arbitrage_opportunities.csv')
    return arbitrage_opportunities

İlk olarak, fonksiyonumuz boş bir 'spreads' sözlüğü oluşturur. Daha sonra onu verilerle dolduracağız.

Bir sonraki adıma geçelim. Fonksiyon, tüm döviz pariteleri ve bunların sentetik analogları üzerinden geçer. Her bir parite için fiyat farkını hesaplar - gerçek Bid fiyatı ile sentetik fiyat arasındaki fark.

spreads[synthetic_pair] = data[pair]['bid'] - synthetic_prices[synthetic_pair]

Bu satır oldukça önemli bir rol oynar. Gerçek ve sentetik fiyat arasındaki farkı bulur. Bu fark pozitifse, bir arbitraj fırsatımız var demektir.

Daha ciddi sonuçlar elde etmek için 0.00008 sayısını kullanıyoruz:

arbitrage_opportunities = pd.DataFrame(spreads) > 0.00008

Bu satır 8 puanın altındaki tüm fırsatları filtreler. Bu şekilde daha yüksek kar olasılığı olan fırsatlar elde edeceğiz.

İşte bir sonraki adım:

arbitrage_opportunities.to_csv('arbitrage_opportunities.csv')

Ardından tüm verilerimizi bir CSV dosyasına kaydediyoruz. Artık bunları inceleyebilir, analiz edebilir, grafikler çizebilir - genel olarak verimli çalışmalar yapabiliriz. Tüm bunlar bu fonksiyon sayesinde mümkün olmaktadır - analyze_arbitrage. Sadece analiz etmekle kalmaz, arbitraj fırsatlarını arar, bulur ve kaydeder.


Test emirlerini açma: open_test_limit_order fonksiyonu

Şimdi open_test_limit_order fonksiyonunu ele alalım. Bizim için emirlerimizi açar.

Fonksiyona göz atalım:

def open_test_limit_order(symbol, order_type, price, volume, take_profit, stop_loss, terminal_path):
    if not mt5.initialize(path=terminal_path):
        print(f"Failed to connect to MetaTrader 5 terminal at {terminal_path}")
        return None
    symbol_info = mt5.symbol_info(symbol)
    positions_total = mt5.positions_total()
    if symbol_info is None:
        print(f"Instrument not found: {symbol}")
        return None
    if positions_total >= MAX_OPEN_TRADES:
        print("MAX POSITIONS TOTAL!")
        return None
    # Check if symbol_info is None before accessing its attributes
    if symbol_info is not None:
        request = {
            "action": mt5.TRADE_ACTION_DEAL,
            "symbol": symbol,
            "volume": volume,
            "type": order_type,
            "price": price,
            "deviation": 30,
            "magic": 123456,
            "comment": "Stochastic Stupi Sustem",
            "type_time": mt5.ORDER_TIME_GTC,
            "type_filling": mt5.ORDER_FILLING_IOC,
            "tp": price + take_profit * symbol_info.point if order_type == mt5.ORDER_TYPE_BUY else price - take_profit * symbol_info.point,
            "sl": price - stop_loss * symbol_info.point if order_type == mt5.ORDER_TYPE_BUY else price + stop_loss * symbol_info.point,
        }
        result = mt5.order_send(request)
        if result is not None and result.retcode == mt5.TRADE_RETCODE_DONE:
            print(f"Test limit order placed for {symbol}")
            return result.order
        else:
            print(f"Error: Test limit order not placed for {symbol}, retcode={result.retcode if result is not None else 'None'}")
            return None
    else:
        print(f"Error: Symbol info not found for {symbol}")
        return None

Fonksiyonumuzun yaptığı ilk şey MetaTrader 5 terminaline bağlanmaya çalışmaktır. Daha sonra işlem yapmak istediğimiz enstrümanın var olup olmadığını kontrol eder.

Aşağıdaki koda bakalım:

if positions_total >= MAX_OPEN_TRADES:
    print("MAX POSITIONS TOTAL!")
    return None

Bu kontrol, çok fazla pozisyon açmamamızı sağlar.

Bir sonraki adım, bir emir açmak için bir talep oluşturmaktır. Burada oldukça fazla parametre var. Emir türü, hacim, fiyat, sapma, sihirli sayı, yorum... Her şey yolunda giderse, fonksiyon bize bunu söyler. Başarısız olursa, bir hata mesajı yazdırır.

open_test_limit_order fonksiyonu bu şekilde çalışıyor. Bu bizim piyasayla olan bağlantımız. Bir bakıma, bir aracı kurumun işlevlerini yerine getirir.


Geçici alım-satım kısıtlamaları: Belirli saatlerde çalışma

Şimdi de işlem saatleri hakkında konuşalım. 

if current_time >= datetime.strptime("23:30", "%H:%M").time() or current_time <= datetime.strptime("05:00", "%H:%M").time():
    print("Current time is between 23:30 and 05:00. Skipping execution.")
    time.sleep(300)  # Wait for 5 minutes before checking again
    continue

Burada neler oluyor? Sistemimiz saati kontrol ediyor. Saat 11:30 ile 05:00 arasını gösteriyorsa, bu saatlerin işlem saatleri olmadığını görür ve 5 dakika boyunca bekleme moduna geçer. Ardından etkinleşir, saati tekrar kontrol eder ve hala erken ise tekrar bekleme moduna geçer.

Buna neden ihtiyacımız var? Bunun için bazı sebepler var. İlk olarak, likidite. Geceleri genellikle daha az olur. İkincisi, makas. Geceleri genişler. Üçüncüsü, haberler. En önemlileri genellikle çalışma saatleri içinde yayınlanır.


Çalışma zamanı döngüsü ve hata işleme

Şimdi 'main' fonksiyonuna bir göz atalım. Bir gemi kaptanı gibidir, ancak dümen yerine bir klavye vardır. Ne yapıyor? Her şey çok basit:

  1. Verileri toplama
  2. Sentetik fiyatları hesaplama 
  3. Arbitraj fırsatlarını arama 
  4. Emirler açma

Ayrıca küçük bir hata işleme de vardır. 

def main():
    data = get_currency_data()
    synthetic_prices = calculate_synthetic_prices(data)
    method_count = 2000  # Define the method_count variable here
    arbitrage_opportunities = analyze_arbitrage(data, synthetic_prices, method_count)

    # Trade based on arbitrage opportunities
    for symbol in arbitrage_opportunities.columns:
        if arbitrage_opportunities[symbol].any():
            direction = "BUY" if arbitrage_opportunities[symbol].iloc[0] else "SELL"
            symbol = symbol.split('_')[0]  # Remove the index from the symbol
            symbol_info = mt5.symbol_info_tick(symbol)
            if symbol_info is not None:
                price = symbol_info.bid if direction == "BUY" else symbol_info.ask
                take_profit = 450
                stop_loss = 200
                order = open_test_limit_order(symbol, mt5.ORDER_TYPE_BUY if direction == "BUY" else mt5.ORDER_TYPE_SELL, price, 0.50, take_profit, stop_loss, terminal_path)
            else:
                print(f"Error: Symbol info tick not found for {symbol}")


Sistem ölçeklenebilirliği: Yeni döviz pariteleri ve yöntemler ekleme

Yeni bir döviz paritesi mi eklemek istiyorsunuz? Bu listeye eklemeniz yeterlidir:

symbols = ["EURUSD", "GBPUSD", "USDJPY", ... , "YOURPAIR"]

Sistem artık yeni pariteyi biliyor. Peki yeni hesaplama yöntemleri?

def calculate_synthetic_prices(data):
    # ... existing code ...
    
    # Add a new method
    synthetic_prices[f'{pair1}_{method_count}'] = data[pair1]['ask'] / data[pair2]['bid']
    method_count += 1


Arbitraj sisteminin geriye dönük test edilmesi

Geriye dönük test hakkında konuşalım. Bu, herhangi bir alım-satım sistemi için gerçekten önemli bir noktadır. Arbitraj sistemimiz de bir istisna değildir.

Biz ne yaptık? Stratejimizi geçmiş veriler üzerinde yürüttük. Neden? Ne kadar verimli olduğunu anlamak için. Kodumuz get_historical_data ile başlar. Bu fonksiyon, MetaTrader 5'ten eski verileri alır. Bu veriler olmadan verimli bir şekilde çalışmamız mümkün olmayacaktır.

Ardından calculate_synthetic_prices gelir. Burada sentetik döviz kurlarını hesaplıyoruz. Bu, arbitraj stratejimizin önemli bir parçasıdır. analyze_arbitrage fonksiyonu bizim fırsat dedektörümüzdür. Gerçek fiyatları sentetik olanlarla karşılaştırır ve farkı bulur, böylece potansiyel kar elde edebiliriz. simulate_trade neredeyse bir alım-satım sürecidir. Ancak, test modunda çalışır. Bu çok önemli bir süreçtir: gerçek para kaybetmektense simülasyonda hata yapmak daha iyidir.

Son olarak, backtest_arbitrage_system hepsini bir araya getirir ve stratejimizi geçmiş veriler üzerinde çalıştırır. Gün be gün, işlem üstüne işlem.

import MetaTrader5 as mt5
import pandas as pd
import numpy as np
import matplotlib.pyplot as plt
from datetime import datetime, timedelta
import pytz

# Path to MetaTrader 5 terminal
terminal_path = "C:/Program Files/ForexBroker - MetaTrader 5/Arima/terminal64.exe"

def remove_duplicate_indices(df):
    """Removes duplicate indices, keeping only the first row with a unique index."""
    return df[~df.index.duplicated(keep='first')]

def get_historical_data(start_date, end_date, terminal_path):
    if not mt5.initialize(path=terminal_path):
        print(f"Failed to connect to MetaTrader 5 terminal at {terminal_path}")
        return None

    symbols = ["AUDUSD", "AUDJPY", "CADJPY", "AUDCHF", "AUDNZD", "USDCAD", "USDCHF", "USDJPY", "NZDUSD", "GBPUSD", "EURUSD", "CADCHF", "CHFJPY", "NZDCAD", "NZDCHF", "NZDJPY", "GBPCAD", "GBPCHF", "GBPJPY", "GBPNZD", "EURCAD", "EURCHF", "EURGBP", "EURJPY", "EURNZD"]
    
    historical_data = {}
    for symbol in symbols:
        timeframe = mt5.TIMEFRAME_M1
        rates = mt5.copy_rates_range(symbol, timeframe, start_date, end_date)
        if rates is not None and len(rates) > 0:
            df = pd.DataFrame(rates)
            df['time'] = pd.to_datetime(df['time'], unit='s')
            df.set_index('time', inplace=True)
            df = df[['open', 'high', 'low', 'close']]
            df['bid'] = df['close']  # Simplification: use 'close' as 'bid'
            df['ask'] = df['close'] + 0.000001  # Simplification: add spread
            historical_data[symbol] = df

    mt5.shutdown()
    return historical_data

def calculate_synthetic_prices(data):
    synthetic_prices = {}
    pairs = [('AUDUSD', 'USDCHF'), ('AUDUSD', 'NZDUSD'), ('AUDUSD', 'USDJPY'),
             ('USDCHF', 'USDCAD'), ('USDCHF', 'NZDCHF'), ('USDCHF', 'CHFJPY'),
             ('USDJPY', 'USDCAD'), ('USDJPY', 'NZDJPY'), ('USDJPY', 'GBPJPY'),
             ('NZDUSD', 'NZDCAD'), ('NZDUSD', 'NZDCHF'), ('NZDUSD', 'NZDJPY'),
             ('GBPUSD', 'GBPCAD'), ('GBPUSD', 'GBPCHF'), ('GBPUSD', 'GBPJPY'),
             ('EURUSD', 'EURCAD'), ('EURUSD', 'EURCHF'), ('EURUSD', 'EURJPY'),
             ('CADCHF', 'CADJPY'), ('CADCHF', 'GBPCAD'), ('CADCHF', 'EURCAD'),
             ('CHFJPY', 'GBPCHF'), ('CHFJPY', 'EURCHF'), ('CHFJPY', 'NZDCHF'),
             ('NZDCAD', 'NZDJPY'), ('NZDCAD', 'GBPNZD'), ('NZDCAD', 'EURNZD'),
             ('NZDCHF', 'NZDJPY'), ('NZDCHF', 'GBPNZD'), ('NZDCHF', 'EURNZD'),
             ('NZDJPY', 'GBPNZD'), ('NZDJPY', 'EURNZD')]

    for pair1, pair2 in pairs:
        if pair1 in data and pair2 in data:
            synthetic_prices[f'{pair1}_{pair2}_1'] = data[pair1]['bid'] / data[pair2]['ask']
            synthetic_prices[f'{pair1}_{pair2}_2'] = data[pair1]['bid'] / data[pair2]['bid']

    return pd.DataFrame(synthetic_prices)

def analyze_arbitrage(data, synthetic_prices):
    spreads = {}
    for pair in data.keys():
        for synth_pair in synthetic_prices.columns:
            if pair in synth_pair:
                spreads[synth_pair] = data[pair]['bid'] - synthetic_prices[synth_pair]

    arbitrage_opportunities = pd.DataFrame(spreads) > 0.00008
    return arbitrage_opportunities

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

def backtest_arbitrage_system(historical_data, start_date, end_date):
    equity_curve = [10000]  # Starting with $10,000
    trades = []
    dates = pd.date_range(start=start_date, end=end_date, freq='D')

    for current_date in dates:
        print(f"Backtesting for date: {current_date.date()}")
        
        # Get data for the current day
        data = {symbol: df[df.index.date == current_date.date()] for symbol, df in historical_data.items()}
        
        # Skip if no data for the current day
        if all(df.empty for df in data.values()):
            continue

        synthetic_prices = calculate_synthetic_prices(data)
        arbitrage_opportunities = analyze_arbitrage(data, synthetic_prices)

        # Simulate trades based on arbitrage opportunities
        for symbol in arbitrage_opportunities.columns:
            if arbitrage_opportunities[symbol].any():
                direction = "BUY" if arbitrage_opportunities[symbol].iloc[0] else "SELL"
                base_symbol = symbol.split('_')[0]
                if base_symbol in data and not data[base_symbol].empty:
                    price = data[base_symbol]['bid'].iloc[-1] if direction == "BUY" else data[base_symbol]['ask'].iloc[-1]
                    take_profit = 800 * 0.00001  # Convert to price
                    stop_loss = 400 * 0.00001  # Convert to price
                    
                    # Simulate trade
                    trade_result = simulate_trade(data[base_symbol], direction, price, take_profit, stop_loss)
                    trades.append(trade_result)
                    
                    # Update equity curve
                    equity_curve.append(equity_curve[-1] + trade_result['profit'])

    return equity_curve, trades

def main():
    start_date = datetime(2024, 1, 1, tzinfo=pytz.UTC)
    end_date = datetime(2024, 8, 31, tzinfo=pytz.UTC)  # Backtest for January-August 2024
    
    print("Fetching historical data...")
    historical_data = get_historical_data(start_date, end_date, terminal_path)
    
    if historical_data is None:
        print("Failed to fetch historical data. Exiting.")
        return

    print("Starting backtest...")
    equity_curve, trades = backtest_arbitrage_system(historical_data, start_date, end_date)

    total_profit = sum(trade['profit'] for trade in trades)
    win_rate = sum(1 for trade in trades if trade['profit'] > 0) / len(trades) if trades else 0

    print(f"Backtest completed. Results:")
    print(f"Total Profit: ${total_profit:.2f}")
    print(f"Win Rate: {win_rate:.2%}")
    print(f"Final Equity: ${equity_curve[-1]:.2f}")

    # Plot equity curve
    plt.figure(figsize=(15, 10))
    plt.plot(equity_curve)
    plt.title('Equity Curve: Backtest Results')
    plt.xlabel('Trade Number')
    plt.ylabel('Account Balance ($)')
    plt.savefig('equity_curve.png')
    plt.close()

    print("Equity curve saved as 'equity_curve.png'.")

if __name__ == "__main__":
    main()

Bu neden önemli? Çünkü geriye dönük test, sistemimizin ne kadar verimli olduğunu gösterir. Karlı mı yoksa bakiyemizi tüketiyor mu? Düşüş ne kadar? Kazançlı işlemlerin yüzdesi ne kadar? Tüm bunları geriye dönük testten öğreniyoruz.

Elbette geçmişteki sonuçlar gelecekteki sonuçları garanti etmez. Piyasa değişiyor. Ancak geriye dönük test olmadan herhangi bir sonuç edinemeyiz. Sonucu bildiğimiz için kabaca ne bekleyeceğimizi de biliyoruz. Bir başka önemli nokta - geriye dönük test, sistemi optimize etmeye yardımcı olur. Parametreleri değiştiriyoruz ve sonuca tekrar tekrar bakıyoruz. Böylece, adım adım sistemimizi daha iyi hale getiriyoruz.

İşte sistemimizin geriye dönük testinin sonucu:

İşte MetaTrader 5'te sistemin bir testi:

Ve işte sistem için MQL5 Uzman Danışmanının kodu:

//+------------------------------------------------------------------+
//|                                                 TrissBotDemo.mq5 |
//|                                  Copyright 2024, MetaQuotes Ltd. |
//|                                             https://www.mql5.com |
//+------------------------------------------------------------------+
#property copyright "Copyright 2024, MetaQuotes Ltd."
#property link      "https://www.mql5.com"
#property version   "1.00"
// Input parameters
input int MAX_OPEN_TRADES = 10;
input double VOLUME = 0.50;
input int TAKE_PROFIT = 450;
input int STOP_LOSS = 200;
input double MIN_SPREAD = 0.00008;

// Global variables
string symbols[] = {"AUDUSD", "AUDJPY", "CADJPY", "AUDCHF", "AUDNZD", "USDCAD", "USDCHF", "USDJPY", "NZDUSD", "GBPUSD", "EURUSD", "CADCHF", "CHFJPY", "NZDCAD", "NZDCHF", "NZDJPY", "GBPCAD", "GBPCHF", "GBPJPY", "GBPNZD", "EURCAD", "EURCHF", "EURGBP", "EURJPY", "EURNZD"};
int symbolsTotal;

//+------------------------------------------------------------------+
//| Expert initialization function                                   |
//+------------------------------------------------------------------+
int OnInit()
{
    symbolsTotal = ArraySize(symbols);
    return(INIT_SUCCEEDED);
}

//+------------------------------------------------------------------+
//| Expert deinitialization function                                 |
//+------------------------------------------------------------------+
void OnDeinit(const int reason)
{
    // Cleanup code here
}

//+------------------------------------------------------------------+
//| Expert tick function                                             |
//+------------------------------------------------------------------+
void OnTick()
{
    if(!IsTradeAllowed()) return;
    
    datetime currentTime = TimeGMT();
    if(currentTime >= StringToTime("23:30:00") || currentTime <= StringToTime("05:00:00"))
    {
        Print("Current time is between 23:30 and 05:00. Skipping execution.");
        return;
    }
    
    AnalyzeAndTrade();
}

//+------------------------------------------------------------------+
//| Analyze arbitrage opportunities and trade                        |
//+------------------------------------------------------------------+
void AnalyzeAndTrade()
{
    double synthetic_prices[];
    ArrayResize(synthetic_prices, symbolsTotal);
    
    for(int i = 0; i < symbolsTotal; i++)
    {
        synthetic_prices[i] = CalculateSyntheticPrice(symbols[i]);
        double currentPrice = SymbolInfoDouble(symbols[i], SYMBOL_BID);
        
        if(MathAbs(currentPrice - synthetic_prices[i]) > MIN_SPREAD)
        {
            if(currentPrice > synthetic_prices[i])
            {
                OpenOrder(symbols[i], ORDER_TYPE_SELL);
            }
            else
            {
                OpenOrder(symbols[i], ORDER_TYPE_BUY);
            }
        }
        
    }
}

//+------------------------------------------------------------------+
//| Calculate synthetic price for a symbol                           |
//+------------------------------------------------------------------+
double CalculateSyntheticPrice(string symbol)
{
    // This is a simplified version. You need to implement the logic
    // to calculate synthetic prices based on your specific method
    return SymbolInfoDouble(symbol, SYMBOL_ASK);
}

//+------------------------------------------------------------------+
//| Open a new order                                                 |
//+------------------------------------------------------------------+
void OpenOrder(string symbol, ENUM_ORDER_TYPE orderType)
{
    if(PositionsTotal() >= MAX_OPEN_TRADES)
    {
        Print("MAX POSITIONS TOTAL!");
        return;
    }
    
    double price = (orderType == ORDER_TYPE_BUY) ? SymbolInfoDouble(symbol, SYMBOL_ASK) : SymbolInfoDouble(symbol, SYMBOL_BID);
    double point = SymbolInfoDouble(symbol, SYMBOL_POINT);
    
    double tp = (orderType == ORDER_TYPE_BUY) ? price + TAKE_PROFIT * point : price - TAKE_PROFIT * point;
    double sl = (orderType == ORDER_TYPE_BUY) ? price - STOP_LOSS * point : price + STOP_LOSS * point;
    
    MqlTradeRequest request = {};
    MqlTradeResult result = {};
    
    request.action = TRADE_ACTION_DEAL;
    request.symbol = symbol;
    request.volume = VOLUME;
    request.type = orderType;
    request.price = price;
    request.deviation = 30;
    request.magic = 123456;
    request.comment = "ArbitrageAdvisor";
    request.type_time = ORDER_TIME_GTC;
    request.type_filling = ORDER_FILLING_IOC;
    request.tp = tp;
    request.sl = sl;
    
    if(!OrderSend(request, result))
    {
        Print("OrderSend error ", GetLastError());
        return;
    }
    
    if(result.retcode == TRADE_RETCODE_DONE)
    {
        Print("Order placed successfully");
    }
    else
    {
        Print("Order failed with retcode ", result.retcode);
    }
}

//+------------------------------------------------------------------+
//| Check if trading is allowed                                      |
//+------------------------------------------------------------------+
bool IsTradeAllowed()
{
    if(!TerminalInfoInteger(TERMINAL_TRADE_ALLOWED))
    {
        Print("Trade is not allowed in the terminal");
        return false;
    }
    
    if(!MQLInfoInteger(MQL_TRADE_ALLOWED))
    {
        Print("Trade is not allowed in the Expert Advisor");
        return false;
    }
    
    return true;
}


Aracı kurumlar için sistemin olası iyileştirmeleri ve yasallığı; limit emirlerini kullanarak likidite sağlayıcısını nasıl etkilememe

Sistemimizin başka potansiyel zorlukları da vardır. Aracı kurumlar ve likidite sağlayıcılar genellikle bu tür sistemlere sıcak bakmazlar. Neden? Çünkü esasen piyasadan gerekli likiditeyi alıyoruz. Hatta bunun için özel bir terim bile ürettiler: Toksik Emir Akışı (Toxic Order Flow). 

Bu gerçek bir sorun. Piyasa emirlerimizle sistemdeki likiditeyi tam anlamıyla emiyoruz. Herkesin bu likiditeye ihtiyacı var: hem büyük oyuncular hem de küçük yatırımcılar. Elbette bunun bazı sonuçları olacaktır.

Bu durumda ne yapmalı? Bir ara çözüm var - limit emirleri. 

Ancak bu tüm sorunları çözmez: Toksik Emir Akışı etiketi, mevcut likiditenin piyasadan emilmesi nedeniyle değil, bu tür bir emir akışına hizmet etmenin getirdiği yüksek yükler nedeniyle konulur. Bu sorunu henüz çözemedim. Örneğin, büyük bir arbitraj işlemi akışına hizmet vermek için 100 USD harcamak ve bundan 50 USD komisyon almak karlı değildir. Belki de buradaki kilit nokta yüksek hacimdir. O zaman aracı kurumlar da iskonto ödemeye hazır olabilir.

Şimdi koda geçelim. Onu nasıl iyileştirebiliriz? İlk olarak, limit emirlerini işlemek için bir fonksiyon ekleyebiliriz. Burada da yapılacak çok iş var - gerçekleştirilmemiş emirleri bekletme ve iptal etme mantığını düşünmemiz gerekiyor.

Makine öğrenimi, sistemi iyileştirmek için ilginç bir fikir olabilir. Sistemimizi hangi arbitraj fırsatlarının işe yarama olasılığının daha yüksek olduğunu tahmin edecek şekilde eğitmenin mümkün olabileceğini düşünüyorum. 


Sonuç

Özetleyelim. Arbitraj fırsatları arayan bir sistem oluşturduk. Sistemin tüm finansal sorunlarınızı çözmediğini unutmayın. 

Geriye dönük test yapmayı ele aldık. Zamana dayalı verilerle çalışır ve daha da iyisi, sistemimizin geçmişte nasıl çalışmış olabileceğini görmemizi sağlar. Ancak unutmayın - geçmiş sonuçlar gelecekteki sonuçları garanti etmez. Piyasa sürekli değişen karmaşık bir mekanizmadır.

Ama en önemlisi ne biliyor musunuz? Kod değil. Algoritma değil. Sizsiniz. Öğrenme, deneme, hata yapma ve tekrar deneme isteğiniz. Bu gerçekten paha biçilemez.

Bu yüzden burada durmayın. Bu sistem, algoritmik alım-satım dünyasındaki yolculuğunuzun sadece başlangıcıdır. Bunu yeni fikirler ve yeni stratejiler için bir başlangıç noktası olarak kullanın. Tıpkı hayatta olduğu gibi, alım-satımda da asıl önemli olan dengedir. Risk ve temkin, açgözlülük ve rasyonellik, karmaşıklık ve basitlik arasındaki denge.

Bu heyecan verici yolculukta iyi şanslar ve algoritmalarınız her zaman piyasanın bir adım önünde olsun!

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

Son yorumlar | Tartışmaya git (7)
pivomoe
pivomoe | 24 Eki 2024 saat 00:31

Lütfen bunun ne hakkında olduğunu açıklayın:

А теперь следующий шаг — список pairs. Это наши валютные пары, которые мы будем использовать для синтеза. Дальше начинается еще один процесс. Мы запускаем цикл по всем парам. Для каждой пары мы рассчитываем синтетическую цену двумя способами:

Делим bid первой пары на ask второй.
Делим bid первой пары на bid второй.
И каждый раз мы увеличиваем наш method_count. В итоге у нас получается не 1000, не 1500, а целых 2000 синтетических цен!

İşte çiftler:

pairs = [('AUDUSD', 'USDCHF'), ('AUDUSD', 'NZDUSD'), ('AUDUSD', 'USDJPY'),
             ('USDCHF', 'USDCAD'), ('USDCHF', 'NZDCHF'), ('USDCHF', 'CHFJPY'),
             ('USDJPY', 'USDCAD'), ('USDJPY', 'NZDJPY'), ('USDJPY', 'GBPJPY'),
             ('NZDUSD', 'NZDCAD'), ('NZDUSD', 'NZDCHF'), ('NZDUSD', 'NZDJPY'),
             ('GBPUSD', 'GBPCAD'), ('GBPUSD', 'GBPCHF'), ('GBPUSD', 'GBPJPY'),
             ('EURUSD', 'EURCAD'), ('EURUSD', 'EURCHF'), ('EURUSD', 'EURJPY'),
             ('CADCHF', 'CADJPY'), ('CADCHF', 'GBPCAD'), ('CADCHF', 'EURCAD'),
             ('CHFJPY', 'GBPCHF'), ('CHFJPY', 'EURCHF'), ('CHFJPY', 'NZDCHF'),
             ('NZDCAD', 'NZDJPY'), ('NZDCAD', 'GBPNZD'), ('NZDCAD', 'EURNZD'),
             ('NZDCHF', 'NZDJPY'), ('NZDCHF', 'GBPNZD'), ('NZDCHF', 'EURNZD'),
             ('NZDJPY', 'GBPNZD'), ('NZDJPY', 'EURNZD')]

İlk çiftin Teklifi nedir? İlk çift:

('AUDUSD', 'USDCHF')
Andrey Khatimlianskii
Andrey Khatimlianskii | 28 Eki 2024 saat 16:09
pivomoe #:

İlk çiftin Teklifi nedir? İlk çift:

AUDUSD aynı zamanda bir çifttir. AUD - USD.

Roman Shiredchenko
Roman Shiredchenko | 28 Eki 2024 saat 20:12
pivomoe #:

Lütfen bunun neyle ilgili olduğunu açıklayın:

İşte çiftler:

İlk çiftin Teklifi nedir? İlk çift:

Sentetikler böyle oluşur. Farklılıkla değil, bölünmeyle. Ve basit değil - ama... okuyun.....
leonerd
leonerd | 21 Kas 2024 saat 10:59
ticks = mt5.copy_ticks_from(symbol, utc_from, count, mt5.COPY_TICKS_ALL)

Hepsi yüklendi. Bu, tiklerde ortaya çıkan şeydir:

array([b'', b'', b'', b'', b'', b'', b'', b'', b'', b'', b'', b'', b'', b'', b'', b'',

...

b'', b'', b'', b'', b'', b'', b'', b'', b'', b'', b'', b'', b'', b'', b'', b'',

b'', b'', b'', b'', b'', b'', b'', b'', b'', b'', b'', b'', b'', b'', b'', b'',

b'', b'', b'', b'', b'', b'', b'', b'', b'', b'', b'', b'', b'', b''],

dtype='|V0')


Ve burada zaten zamanında bir çıkış alıyoruz:

ticks_frame['time'] = pd.to_datetime(ticks_frame['time'], unit='s')
leonerd
leonerd | 22 Kas 2024 saat 09:11

https://www.mql5.com/tr/docs/python_metatrader5/mt5copyticksfrom_py örneğindeki kod da çalışmıyor

>>>  timezone = pytz.timezone("Etc/UTC")
>>>  utc_from = datetime(2020, 1, 10, tzinfo=timezone)
>>>  ticks = mt5.copy_ticks_from("EURUSD", utc_from, 100000, mt5.COPY_TICKS_ALL)
>>>
>>> print("Alınan keneler:",len(ticks))
Получено тиков: 100000
>>> print("Ortaya çıkan keneleri olduğu gibi kabul edelim.")
Выведем полученные тики как есть
>>>  count = 0
>>> for tick in ticks:
...     count+=1
...     print(tick)
...     if count >= 100:
...         break
...
b''
b''
b''
b''

Her neyse, python nasıl bir şey? Nasıl hazırlanır? Belli değil...

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.
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.
İş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 II): Gann Karesi göstergesi oluşturma William Gann yöntemleri (Bölüm II): Gann Karesi göstergesi oluşturma
Zaman ve fiyatın karesi alınarak oluşturulan Gann'ın 9’un Karesine dayalı bir gösterge oluşturacağız. Kodu hazırlayacağız ve göstergeyi platformda farklı zaman aralıklarında test edeceğiz.