İşlemlerde Python: veri analizi, otomasyon ve araştırma

Python, piyasa verilerini düzenlemenize, işlem günlüğünü analiz etmenize ve formülleri elle kopyalamadan hesapları yinelemenize yardımcı olur. Özellikle hesap tablosu yetersiz kaldığında veya bakımı zorlaştığında yararlıdır. Bu makale ortam kurulumundan veri yükleyen, kaliteyi denetleyen ve sonucu kaydeden betiğe götürür. Ayrıca notebook araştırmasının işlem hesabına bağlı yazılımdan nasıl ayrıldığını ve bu entegrasyonun hangi korumaları gerektirdiğini açıklar.

Python, tekrarlanabilir veri analizini ve sonuç kontrollerini destekler; kod, veri kalitesinin veya sağlam test metodolojisinin yerini alamaz.
60 saniyede temel noktalar
  • Araştırmadaki ve gerçekleşmedeki Python farklı sorumluluk düzeyleridir — istatistik hesaplayan notebook sonuç doğurmadan çökebilir. Hesaba dokunan kod limit, günlük, zaman aşımı, test modu ve kontrollü durdurma gerektirir
  • Ortam yeniden üretilebilir olmalıdır — Python sürümü, yalıtılmış sanal ortam, sabitlenmiş kitaplık sürümleri, veri kaynağı, saat dilimi ve tarih aralığı. Bunlar olmadan “benim makinemde çalışıyor” sonuç değil anekdottur
  • OHLCV analizinde pandas ve numpy tablonun yerini alır — M1'den H4'e yeniden örnekleme, hareketli ortalama, düşüş, pariteler arası korelasyonlar. Hesap tablosu 1048576 satırda biter; tik verisi bunun çok üzerinde başlar
  • Anahtar ve parolalar asla koda yazılmaz — yalnızca ortam değişkeni veya depo dışında tutulan yapılandırma dosyası. Betiğe yazılan anahtar Git geçmişine girer ve her “silme” sonrasında orada kalır
  • API sınırsız erişim değildir — kimlik doğrulaması, istek sıklığı limiti, hata kodları vardır. Test ortamı bazen — her zaman değil — ayrı ana makinededir. HTTP 429 işlemesi ve zaman aşımı olmayan kod er ya da geç en kötü anda kesilir
  • Geleceği görme hatası kodda tek bir kaydırmadır — mum kapanışında kararlaştırılan pozisyon yalnızca sonraki mumun getirisinde çalışır. Sinyali aynı satırın getirisiyle çarpmak, piyasanın hiç sunmadığı sonuç üretir
  • Çalıştırılmamış kod denetlenmemiş koddur — derlenmesi ve “doğru görünmesi”, veri üzerinde çalışması değildir. Altı çalıştırılabilir blok, açıklanan veri ve taklitlerle çevrimdışı kontrol edildi. Çıktı blokları gerçek test sonuçlarını gösterir; kurulum komutları ortamı anlatır, emir gönderme ise açıkça çalıştırılamayan yapısal taslaktır

1. Python nerede yardımcı olur, nerede yalnızca çözüm gibi görünür?

Bir hesap tablosu, işlem günlüğünü sabrınızın yettiği sürece idare eder. Bu sınırın nerede olduğu makineye, formül sayısına ve tablonun yapısına bağlıdır; dolayısıyla yuvarlak bir işlem sayısı bu sınırı tarif edemez. Ancak dosya biçimi belirtiminde yazdığı için hiçbir şeye bağlı olmayan katı bir sınır vardır: tek Excel çalışma sayfası 1048576 satır ve 16384 sütun tutar [16]. Tek bir döviz çiftinin bir yıllık tik geçmişi bu sayının katlarıdır ve hiçbir yöntem bu sınırı ortadan kaldırmaz.

Python burada sihir değildir. Tablonun çözemediği veya saatlerce işle çözdüğü dört somut sorunu çözer:

  • Veri ölçeği — sayfa satırı değil, bellek ve saklama biçimi sınırlar
  • Tekrarlanabilirlik — aynı veride yarın çalıştırılan aynı betik aynı sonucu verir. Elle sürüklenmiş formüllü tablo bunu garanti etmez
  • Elle uğraşmadan aralık değiştirme — OHLCV korunarak M1'den H4'e toplamak tek bir resample çağrısıdır; yardımcı formüller sütunu değil
  • Veri çekme ve temizliği otomatikleştirme — zamanlanmış betik siz yokken veriyi indirir, denetler, saklar

Hepsi bu. Python piyasa avantajı vermez, strateji kalitesini yükseltmez, broker gerçekleşmesini değiştirmez. Soruyla yanıtın arasını kısaltır; tam da bu yüzden fikrin işlemediğini daha çabuk gösterir.

Hesap tablolarıyla Python'u beş çalışma ölçütü üzerinden karşılaştıran tablo
Görevin ölçeğine ve tekrarlanabilirliğine göre seçim yapın; strateji kalitesini ayrıca değerlendirin.

Python neleri yapmaz? Mikrosaniyelerin önemli olduğu çok düşük gecikmeli işlem için uygun değildir; çöp toplayıcılı yorumlanan dil kötü seçimdir. Ama “Python işlem için çok yavaş” da yanlıştır: sonraki H1 mumu açılışında giren stratejide Python hesap süresi, emrin ağ ve broker sunucusundan geçişine göre önemsizdir.

Vektörleştirme hızını sihirden değil mekanizmadan alır: sütun işlemi yorumlayıcı döngüsü yerine derlenmiş numpy kodunda çalışır. Farkın ölçeği veri, işlem ve donanıma bağlıdır. Sayı verecekseniz kendi makinenizde standart kitaplığın timeit modülüyle ölçün [4] ve kitaplık sürümleriyle kaydedin. Ortamsız başkasının benchmark'ını aktarmak ölçüm değildir.

Yatırımcının analiz araçları karşılaştırması:

ÖzellikExcelPythonPine ScriptMQL4/MQL5
Veri sınırıSayfa başına 1048576 satırMakine belleğiyle sınırlıTradingView planına bağlıBrokerın sağladığı geçmiş
Kodun çalıştığı yerYerelYerel, sunucu veya bulutTradingView ortamındaMetaTrader terminalinde
Hesap erişimiYokYalnızca broker API'si veya aracı paket üzerindenYok; uyarı sonraki zinciri tetikleyebilirTerminal içinde yerleşik
Veri kaynağıYüklediğiniz her şeyHer şey; ancak kaynağı adlandırmalısınızTradingView akışıBroker akışı
Yeniden üretilebilirlikDüşük (manuel formüller)Sürümler sabitlenirse yüksekOrta (Pine sürümü, platform verisi)Orta (terminal build'i, broker verisi)
Öğrenme eğrisiDüşükOrtaDüşük–ortaOrta–yüksek

Tablonun son satırındaki ayrım, göründüğünden daha önemlidir. Pine Script, TradingView ortamının içinde; MQL, MetaTrader terminalinin içinde; Python ise siz nerede çalıştırırsanız orada yürütülür. Bu özgürlük, ortamın sorumluluğunu size verir: kitaplık sürümleri, sistemin saat dilimi, kesintisiz çalışma ve betiğin hesaba erişmeye hakkının olup olmadığı.

Makalenin sınırları. Bu bir Python veya geriye dönük test kursu değildir. Eğitim/doğrulama örneklemi ayrımı, forward test ve maliyet modeli gibi test yöntemi Demo hesaplar, geriye dönük test ve forward test makalesine aittir. Terminal kodu ve sunucuda sürekli çalıştırılması İşlem otomasyonu: EA'lar, betikler ve VPS makalesinde anlatılır. Dil modelleri ve sınırları ise İşlemlerde yapay zekâ ve LLM'ler makalesindedir. Burada yalnızca veri, hesap ve entegrasyon katmanı olarak Python'u ele alıyoruz.
Hesap tablosunu bırakmanız gerekmez. Python sonuçlarını CSV'ye aktarıp tabloda açarsınız; veya tersine, işlem günlüğünüzü tabloda tutup analiz için pandas'a alırsınız. openpyxl kitaplığı .xlsx dosyalarını Python'dan doğrudan okur/yazar; en son sürümü Haziran 2024'tür (3.1.5) [13]; bu özel durumda eski sürüm tarihi bir uyarı işareti değildir. .xlsx biçimi yerleşiktir ve kitaplık ne NumPy'ye ne de pandas'a bağlıdır. Bu nedenle bölüm 2'deki kural onu dışlamaz; yalnızca bu durumu bilmenizi ister [DURUM: 2026-08-22]. Python, tablonun bittiği yerde onu genişletir.

2. Yeniden üretilebilir ortam: sürümler, venv, requirements

Başkasının betiğinin “çalışmamasının” yaygın nedeni kod değil, yazarın farklı kitaplık sürümleri kullanıp bunu kaydetmemesidir. Piyasa veri analizinde bu rahatsızlık değildir; sonuç tekrar üretilemediğinden onu geçersiz kılar.

Altı şeyi söyleyebiliyorsanız ortam yeniden üretilebilirdir: Python sürümü, yalıtım yöntemi, sabit bağımlılıklar, tarih aralığıyla veri kaynağı, saat dilimi ve rastgeleliğe bağlı her yerde tohum. Eksik unsur, sonucu ölçüm değil hikâye yapar.

Sanal ortam isteğe bağlı değildir. Kitaplıkları “genel” olarak kurmak, bir projeyi güncellemenin diğerini bozduğu bir duruma yol açar. Yalıtım ücretsizdir, yaklaşık bir düzine saniye sürer ve standart kitaplığın venv modülüne dayanır [4]:

# Windows, PowerShell
py -3.12 -m venv .venv
.venv\Scripts\Activate.ps1

# macOS / Linux
python3.12 -m venv .venv
source .venv/bin/activate

# iki sistem için ortak
python -m pip install --upgrade pip
python -m pip install pandas numpy matplotlib requests python-dotenv
python -m pip freeze > requirements.txt

En sık atlanan satır sonuncusudur. pip freeze gerçekte kurulan her şeyin tam sürümünü, bilmediğiniz bağımlılıklar dâhil kaydeder. Dosyayı kodla birlikte depoya koyarsınız. Diğer makinede ortamı yeniden kurmak böylece python -m pip install -r requirements.txt olur; tahmin yürütmek değil.

Güncel çevrimdışı doğrulamada kullanılan ortam bölüm 4 ve 9'daki hesaplama örnekleri için: Windows 11, Python 3.12.10, pandas 3.0.5 ve NumPy 2.5.3; 8 Ekim 2026 tarihinde kontrol edildi. Aşağıdaki liste bu hesaplarla ilişkili paketleri kaydeder. Makinede kurulu her paketin dışa aktarımı değil, sınırlı bir envanterdir[1][3].

# Yerel hesap doğrulamasında kullanılan bağımlılık sürümleri
numpy==2.5.3
pandas==3.0.5
python-dateutil==2.9.0.post0
six==1.17.0
tzdata==2026.4

# yorumlayıcı
Python 3.12.10

pandas'ın başka paketler de kullandığına dikkat edin: ortamın açıklaması tek bir kitaplık sürümünden ibaret değildir. Bağımlılıklar makineler arasında farklara neden olabilir. Yukarıdaki kurulum komutları, bir proje hazırlama prosedürünü örnekler; bu denetim sırasında gerçekleştirilmiş kurulumun kaydı değildir. Eksiksiz requirements.txt dosyasını kendi ortamınızda python -m pip freeze ile oluşturup kodla tutun.

Bu dosya da tüm makalenin ortamını eksiksiz açıklamaz; yalnızca hesaplama bölümlerinin ortamını anlatır ve böyle okunmalıdır. Bölüm 7'de HTTP hata işlemesini kontrol eden parça, ortama requests 2.34.2 eklenerek çalıştırıldı. Bölüm 8'deki güvenlik kapısı testi ise standart kitaplık dışında hiçbir paket gerektirmez. matplotlib ve python-dotenv sürümleri verilmez; çünkü gerçekten çalıştırılan örneklerin hiçbirinde kullanılmadılar. Sırf “düzenli görünsün” diye bunları listelemek, kimsenin kontrol etmediği bir sayı yazmak olurdu. Bütün çalışma için ortam kuruyorsanız yukarıdaki kurulum komutunu başlangıç kabul edin ve kendi pip freeze çıktınızı sabitleyin; sonuçlarınızı anlatan tek bağımlılık dosyası odur.

UTC verileri, eksik değer kontrolleri ve sonuç dışa aktarımı içeren, emir göndermeyen kavramsal araştırma notebook'u.
Notebook, yeniden üretilebilir veri analizini destekler. Çıktısı, ayrı bir emir gerçekleştirme sisteminde kullanılmadan önce doğrulanmalıdır.

Notebook mu, .py dosyası mı? İkisi de kullanılır, ancak farklı amaçlarla. Jupyter, henüz ne aradığınızı bilmediğinizde uygundur: hücreyi çalıştırır, sonuca bakar, parametreyi değiştirirsiniz. Ancak onu emir gerçekleştirme kodu için uygunsuz kılan bir özelliği vardır: değişkenlerin durumu dosyada yazılı sıraya değil, hücrelerin çalıştırıldığı sıraya bağlıdır. Hücreleri 1–3–2 sırasıyla çalıştırılmış bir notebook, sırayla çalıştırılmış olanla aynı görünür. Hesaba erişen kod, düz bir .py dosyası olmalı; baştan sona çalıştırılmalı, testleri ve işlem kaydı bulunmalıdır.

Anaconda ve Google Colab kendi bedelleri olan kısayollardır. Anaconda büyük bir paket kümesini bir kerede kurar ve kendi bağımlılık yöneticisine sahiptir. Bu, başlangıcı kolaylaştırır; ancak conda ile pip birlikte kullanıldığında ortamı kesin biçimde açıklamayı zorlaştırır. Colab, kurulum gerektirmeden tarayıcıda çalışır ve öğrenmeye uygundur; fakat her oturumda ortam sıfırdan yeniden oluşturulur ve kitaplık sürümleri müdahaleniz olmadan değişir. Bir yıl sonra da tekrarlanması gereken materyal için ikisi de ek disiplin gerektirir.

Başlangıç kitaplıkları — kırk değil dört kalem (ilk kalem çift olduğundan beş paket):

  • pandas ve numpy — veri yapıları ve hesaplama
  • matplotlib — grafikler
  • requests — API ile HTTP iletişimi
  • python-dotenv — depo dışındaki dosyadan yapılandırma yükleme

Teknik göstergeleri gerçekten gerektiğinde ekleyin. Eski rehberlere bir düzeltme: TA-Lib PyPI'da C kitaplığını içeren resmî ikili wheel'ler yayımlar. Desteklenen işletim sistemi, mimari ve Python birleşiminde yalnızca pip install TA-Lib ne gayriresmî indirme ne kendi derlemenizi gerektirir[9]. Kurulumdan önce ilgili wheel'i kontrol edin; her bilgisayarda çalışma sözü değildir. 8 Ekim 2026'da okunan meta veride TA-Lib 0.8.1, pandas-ta ise hâlâ Eylül 2025'te yayımlanan 0.4.71b0 görünür[13]. İkincisi numba==0.61.2 sürümüne sabitlenir; yalnızca pandas gereksinimini kontrol etmek yetmez. Python 3.14.3 üzerinde 22 Ağustos 2026 tarihli geçmiş kurulum testi bu bağımlılıkta only versions >=3.10,<3.14 are supported hatasıyla başarısız oldu. Bu gözlem, Python 3.12.10 üzerinde çalışan güncel örneklere otomatik olarak uygulanmaz. Mevcut denetim PyPI meta verilerini okudu; TA-Lib veya pandas-ta'yı kurmadı ya da çalıştırmadı. Bu nedenle yanındaki kontrol kuralı fazla dardır: yakın tarihli bir sürüm ve uyumlu pandas gereksinimi, bağımlılık kümesinin tamamının kurulup doğru çalışabileceğini kanıtlamaz. Sabitlenmiş derlenmiş kitaplıkları, yorumlayıcınız için wheel kullanılabilirliğini ve küçük veriyle testi de kontrol edin. Son iki sürümün meta verileri projenin bütün geçmişini anlatmaz; önceki boşlukları kendi deposunda değerlendirin.

Kitaplık eklemeden önce son sürüm tarihini kontrol edin. Uzun süre geliştirilmemiş proje er geç yeni NumPy/pandas'tan uzaklaşır; eski sürümleri ayrı ortamda sabitler veya kodu yeniden yazarsınız. Son sürüm tarihi PyPI paket sayfasında görünür [13] ve GitHub yıldızlarından daha çok şey söyler. Özellikle eski backtest çerçevelerinde geçerlidir.

Rastgelelik tohumu. Rastgeleliğe bağlı her yerde — örneklem ayırma, bootstrap, simülasyon, model başlatma — tohumu belirleyip kaydedin. numpy'da: rng = np.random.default_rng(20260822) [3]; sonra yalnızca bu nesneyi kullanın. Yoksa aynı betiğin iki çalışması farklı sonuç verir, hangisini anlattığınız belirsizleşir.

3. pandas ve numpy: OHLCV, zaman dizini, saat dilimleri

numpy, sayı dizileri üzerindeki hesaplamayı; pandas ise yapıyı yönetir. DataFrame, adlandırılmış sütunları ve çoğunlukla zamana dayalı bir dizini olan bir tablodur. Piyasa verisiyle çalışırken zaman dizini hesaplamaların kendisinden daha önemlidir; çünkü düşündüğünüz şeyi gerçekten hesaplayıp hesaplamadığınızı belirler.

Yükleme – pd.read_csv() milyonlarca satırlı dosyaları işler, ayraçları tanır, sütunu belirtirseniz tarihleri ayrıştırır. Ama varsayılan olarak saat dilimini bilmez; ilk çözülecek konu budur.

Aralık değiştirme — OHLCV korunarak M1'i H4'e toplamak tek çağrıdır: df.resample('4h').agg({'open':'first', 'high':'max', 'low':'min', 'close':'last', 'tick_volume':'sum'}). Genellikle ayarlanmayan ama yorumu belirleyen iki argümana dikkat edin: label pencerenin başlangıç mı bitiş mi etiketi aldığını belirler; closed ise hangi kenarın pencereye ait olduğunu belirler. Çoğu sıklıkta varsayılan etiket sol kenardadır [1]; yani mum zamanı açılışıdır. pandas 3.0.5'te resample('4h') gece yarısından başlayan sekiz saatlik mumda 00:00 ve 04:00 etiketleri verir. Sinyaliniz kapanış varsayıyor, veri açılış etiketi taşıyorsa fark etmeden tüm sonucu tek aralık kaydırırsınız.

Hareketli istatistikler – df['close'].rolling(20).mean() bir hareketli ortalama verir, .rolling(20).std() şu bantlarda kullanılan sapmayı verir: Bollinger Bantları, .ewm(span=20).mean() üstel ortalama verir [1]. Hepsi geçerli satıra göre yalnızca geçmiş veriden hesaplanır; geri kalan her şeyin doğruluğu buna dayanır.

Filtreleme — birleşik koşul tek kez yazılır: df[(df['sma20'] > df['sma50']) & (df['rsi'] < 30)]. Her koşulun parantezi zorunludur; çünkü & Python'da karşılaştırmadan daha sıkı bağlanır; parantezi atlamak sessiz yanlış yanıt değil hata verir. Bu noktada bu iyi haberdir.

Dört sentetik OHLCV mumunu ve veri kontrollerini gösteren tutarlı tablo
DataFrame biçimi veri kalitesini kanıtlamaz; değerleri, zamanı ve hacmin tanımını kontrol edin.

Saat dilimi ayrıntı değil karardır. Broker verileri genellikle broker sunucusunun zaman damgasını taşır; harici sağlayıcı verileri çoğunlukla UTC'dir. İşletim sistemi ise seçilen bölgenin saat değişikliklerini izleyen yerel saati kullanabilir. Bu üç saat birbirine karışırsa “Londra seansı” analizi Londra seansından başka bir şeyi incelemiş olur.

Pratik kural önce kaynak saat dilimini belirlemek, hesapları UTC'de yapmaktır. Açık ofset veya Z olan damgalarda pd.to_datetime(column, utc=True) kullanın. Ofsetsiz sunucu damgalarını önce ayrıştırın, doğrulanmış bölgeyi tz_localize() ile atarken belirsiz ve var olmayan saatleri ele alın; sonra tz_convert("UTC") uygulayın. Yalnızca utc=True tek başına kullanmak, saat dilimi belirtilmemiş değerleri UTC kabul eder; brokerın saat dilimini çıkarmaz. Veriyi Europe/London saat dilimine yalnızca sunum için dönüştürün[1]. Saat dilimi açık dizin ve kayıtlı kaynak serileri tutarlı birleştirmenizi sağlar.

Broker sunucu saati ayrı değerdir. Birçok broker, sunucusunu UTC'ye göre ofsetli bir saat diliminde çalıştırır; bu ofset bazen ABD veya Avrupa'daki saat değişikliğiyle birlikte değiştirilir. Ofseti ezberden varsaymayın: brokerın teknik bilgilerinden kontrol edin ve proje yapılandırmasında bir parametre olarak kaydedin; koda yazılmış sabit bir sayı olarak değil. [ÖZELLİK BROKERA/HESABA BAĞLI]

Eksik veri. OHLCV geçmişinde boşluklar bulunur: hafta sonları, tatiller, teknik aralar ve enstrümanın kotasyon almadığı dönemler. pandas'ın sunduğu df.ffill() bu boşlukları son bilinen değerle doldurur. Fiyat verisinde bu genellikle kötü bir fikirdir; çünkü sıfır oynaklıklı mumlar oluşturur ve standart sapmayı düşürür. Eksik dönemleri kaldırıp kaç tane olduklarını saymak daha iyidir.

Eski materyale düzeltme. Şu biçim: df.fillna(method='ffill') birkaç yıllık rehberlerde yaygındır ama kaldırılmıştır: method parametresi, fillna() içinde pandas 2.1'de kullanımdan kaldırılacak işaretlendi [2] ve pandas 3.0'da kaldırıldı. Eski biçim artık uyarı değil şu hatayla program sonlanması üretir: TypeError: NDFrame.fillna() got an unexpected keyword argument 'method'. Doğru biçimler: df.ffill() ve df.bfill(). pandas 3.0.5 imzası artık parametreyi içermez: DataFrame.fillna(value, *, axis=None, inplace=False, limit=None) [1]. İmza ve hata metni belgelerden okunmadı, pandas 3.0.5 üzerinde çalıştırılarak kontrol edildi. [DURUM: 2026-08-22]
Tick hacmi piyasa hacmi değildir. Merkezsiz döviz piyasasında bireysel müşteri birleştirilmiş piyasa işlem hacmini almaz; belirli bir brokerın veri akışındaki kotasyon değişimlerinin sayısını alır. Bu, etkinliğin dolaylı göstergesi olarak ihtiyatla kullanılabilir. Ancak dosyadaki “volume” adlı sütun piyasa hacmi değildir ve brokerlar arasında karşılaştırılabilir değildir.

Aynı anda birden çok araç. Döviz portföyü analizi için pandas hiyerarşik dizin (MultiIndex) sunar: tek DataFrame, parite ve zamanla dizinlenmiş çok sayıda çiftin verisini tutar. Bu, bütün seriler önce aynı saat dilimine ve aynı zaman damgası ızgarasına getirildiği takdirde, enstrümanlar arasındaki korelasyonu ortak zaman ekseninde hesaplamanızı sağlar. Aksi halde birbirine göre kaydırılmış veriler arasında korelasyon hesaplamış olursunuz.

Sonuçları kaydetme. df.to_csv() okunur ve evrenseldir. df.to_parquet() küçük dosya ve hızlı okuma sağlar; sütun türleriyle dizin bölgesini de korur [1] — CSV ise her şeyi metin olarak yazar; yeniden yüklerken ayrıştırmayı tekrarlamak gerekir. Yüksek hacimli çalışma verilerinde Parquet daha kullanışlıdır; ancak ek bir yazma motoru gerektirir. Bu nedenle onu da şu dosyaya ekleyin: requirements.txt.

Python'da makro bağlam. Pozisyonlanma raporu ve takvim verisi fiyat gibi çekilebilir; saat dilimi ve yayımlanma anı disiplini aynıdır. Yorumlamak kursun başka bölümündedir: COT raporları ve pozisyonlanma.

4. Asgari örnek: CSV → UTC → getiriler → boşluk kontrolü → kayıt

İlk betiğiniz emir göndermemeli, hatta brokera bağlanmamalıdır. Yararlı, denetlenebilir ve tamamen güvenli bir şey yapmalıdır: dosya yüklemek, zamanı düzenlemek, getirileri hesaplamak, veride neyin yanlış olduğunu açıkça bildirmek ve sonucu kaydetmek.

Varsayımlar. Şu sütunlu CSV dosyası: time,open,high,low,close,tick_volume. time sütunu, bölge işaretli ISO 8601 biçimindedir (Z UTC anlamına gelir). Aralık saatlik olarak bildirilmiştir. Çalıştırmada kullanılan dosya, gerçek dışa aktarımlarda görülen üç kusuru bilerek içerir: yinelenen bir zaman damgası, eksik bir saat ve boş bir hacim hücresi. Bu altı satır doğrudan koda yapıştırılmıştır; böylece hiçbir dosya indirmeden örneği çalıştırabilir ve tam olarak aynı sonucu alabilirsiniz. Yeniden üretilemeyen sonuç, sonuç değildir. Ortam: Python 3.12.10, pandas 3.0.5, numpy 2.5.3, Windows 11, [DURUM: 2026-10-08].

"""Asgari örnek: CSV -> UTC -> log getiri -> boşluk kontrolü -> kayıt."""
import io
from pathlib import Path

import numpy as np
import pandas as pd

# Dosya indirmeden sonucu üretmek için veri kodun içinde.
# Altı satırda üç kasıtlı kusur: mükerrer 01:00 damgası,
# eksik 03:00 saati ve 02:00'de boş hacim hücresi.
CSV_DATA = """time,open,high,low,close,tick_volume
2026-06-01T00:00:00Z,1.08140,1.08170,1.08131,1.08153,812
2026-06-01T01:00:00Z,1.08153,1.08190,1.08150,1.08176,640
2026-06-01T01:00:00Z,1.08153,1.08195,1.08149,1.08182,655
2026-06-01T02:00:00Z,1.08182,1.08188,1.08130,1.08141,
2026-06-01T04:00:00Z,1.08120,1.08125,1.08040,1.08050,903
2026-06-01T05:00:00Z,1.08050,1.08061,1.07980,1.07993,774
"""

OUTPUT = Path("EURUSD_H1_returns.csv")
INTERVAL = "1h"  # giriş verisinin ilan edilmiş sıklığı


def load_data(source) -> pd.DataFrame:
    """CSV'yi yükler, UTC'de saat dilimli dizin kurar.

    Bilerek sıralamaz: dosyadaki satır sırası aktarım kalitesinin
    bilgisidir. Sıralama rapordan sonra yapılır.
    """
    df = pd.read_csv(source)
    # utc=True tek bölgeyi zorlar; yoksa pandas bölgesiz dizin döndürebilir
    df["time"] = pd.to_datetime(df["time"], utc=True, format="ISO8601")
    return df.set_index("time")


def quality_report(df: pd.DataFrame, interval: str) -> dict:
    """Hesap öncesi görülmesi gereken sayaçlar."""
    expected = pd.date_range(df.index.min(), df.index.max(), freq=interval, tz="UTC")
    return {
        "rows": int(len(df)),
        "index_duplicates": int(df.index.duplicated().sum()),
        "missing_cells": int(df.isna().sum().sum()),
        "missing_timestamps": int(len(expected.difference(df.index))),
        # Sıralamadan ÖNCE ölçülür; sort_index() sonrasında bu kontrol daima
        # doğru olur, bozuk zaman sıralı dosyayı hiç bulamazdı.
        "file_chronology_ok": bool(df.index.is_monotonic_increasing),
    }


def add_returns(df: pd.DataFrame) -> pd.DataFrame:
    """Log getiri = ln(close_t / close_(t-1)). Birim: doğal logaritma, boyutsuz."""
    df = df[~df.index.duplicated(keep="last")].copy()
    df["log_return"] = np.log(df["close"]).diff()
    return df


def main() -> None:
    df = load_data(io.StringIO(CSV_DATA))  # uygulamada: load_data(Path("EURUSD_H1.csv"))
    print("kalite kontrolü:", quality_report(df, INTERVAL))
    df = add_returns(df.sort_index())
    print("mükerrerler sonrası mum:", len(df))
    print("log getiri toplamı:", round(float(df["log_return"].sum()), 6))
    print("log getiri standart sapması (1h ve 2h aralıkları):",
          round(float(df["log_return"].std()), 6))
    df.to_csv(OUTPUT)
    print("kaydedildi:", OUTPUT)


if __name__ == "__main__":
    main()

Çalıştırma çıktısı verilen veri ve ortam içindir; birebir yeniden üretebilirsiniz:

kalite kontrolü: {'rows': 6, 'index_duplicates': 1, 'missing_cells': 1, 'missing_timestamps': 1, 'file_chronology_ok': True}
mükerrerler sonrası mum: 5
log getiri toplamı: -0.00148
log getiri standart sapması (1h ve 2h aralıkları): 0.000467
kaydedildi: EURUSD_H1_returns.csv

Yorum. Betik, herhangi bir hesaplamadan önce yerleştirilmiş üç kusurun tamamını tespit etti; bütün değeri budur. Log getirilerin toplamı −0.00148 çıktı. Yinelenenler kaldırıldıktan sonra beş mum, yani ardışık gözlenen fiyatlar arasında dört aralık kaldığı için bu, incelenen pencerede kurun yaklaşık yüzde 0.15 düşmesi demektir. Altı, mumların değil dosyadaki satırların sayısıdır; bir satır yinelenmiştir. 0.000467 standart sapma ise tek H1 mumu başına bir niceliktir; yıllık veya günlük değildir. Aralık belirtilmeden başka bir nicelikle karşılaştırılması anlamsızdır. Log getiri boyutsuzdur ve zaman boyunca toplanabilir; basit getirilerden farklı olarak çarpmak yerine toplamanızın nedeni budur.

Sınırlamalar. Beş mum, istatistiksel örneklem değil, işleyişi gösteren bir örnektir; bu kadar az gözlemden piyasa hakkında çıkarım yapılamaz. Betik spread, komisyon veya kayma modellemez; çünkü geriye dönük test değildir. Hesap sonucunu değil, fiyat getirilerini hesaplar. Eksik zaman damgasını tespit etmek, verilerin gerçekten bildirilen saatlik sıklıkta olduğunu varsayar. Hafta sonunu kapsayan verilerde “eksik” damgaların sayısı büyük ve tamamen normal olur; bu nedenle uyarı eşiği işlem takvimine göre belirlenir. Ayrı bir tuzak ölçümün kendisindedir: betik boşluğu tespit eder ama doldurmaz; dolayısıyla eksik saatin üzerinden hesaplanan getiri bir değil iki aralığı kapsar. Bu örnekte dört getiriden biri böyledir; yine de bütün sonuç “H1 mumu başına” diye etiketlenmiştir. Bu kesin ölçüm değil, basitleştirmedir. Dürüstçe iki yol vardır: veriyi tam saatlik ızgaraya yeniden dizinleyip boşlukta eksik değer bırakmak veya farklı zaman ufuklarının karışmasını ölçümün açık bir sınırlaması olarak raporlamak.

Zaman sırası neden sıralamadan önce denetlenir? Eğer load_data() bir sort_index() çağrısıyla bitseydi file_chronology_ok alanı her zaman doğru olurdu. Raporda duyurulan kontrol hiçbir şeyi tespit edemez, sırası karışmış dosya iz bırakmadan geçerdi. Başarısız olamayan kontrol, hiç kontrol olmamasından kötüdür; çünkü sahte bir doğrulama duygusu verir. Bu yüzden sıra, dosyadan yüklendiği haliyle veri üzerinde ölçülür; sıralama yalnızca rapordan sonra yapılır.

Doğrulama durumu: bu parça belirtilen ortamda çalıştırıldı, çıktı gerçek program çıktısıdır. Başka pandas sürümünde sözlük biçimi veya yuvarlama farklı görünebilir.

5. Görselleştirme: grafikte tek soru

Sonucu özetleyen tek sayı, en önemli şeyleri gizler: strateji ne zaman kaybetti, kayıp ne kadar sürdü ve bütün sonuç üç işleme mi dayanıyor? Grafik, raporda bir süs değil, veriye tablonun yanıtlamadığı bir soru sorma yoludur.

matplotlib Python'un temel grafik kitaplığıdır; statik ve sözdizimi biraz hantaldır. plotly etkileşimli grafikler sunar: noktaya gelerek tarihi ve değeri görür, bir kesite yakınlaşır, HTML dosyasına aktarabilirsiniz. Çok büyük veri kümelerinde daha yavaş olabilir ve ayrı kurulum gerektirir. Notebook içinde keşif için plotly, rapora kaydedilen grafikler için matplotlib daha kullanışlıdır.

Çizmeyi bilmeye değer dört grafik; her biri farklı soruyu yanıtlar:

  1. Özkaynak eğrisi — zaman içinde birikmiş sonuç. “Büyüdü mü, ne zaman?” der; “ne kadar kazanacağım?” değil
  2. Düşüş grafiği — her anda o ana kadarki zirveden uzaklık. En derin düşüşü ve toparlanması için gereken süreyi gösterir. Hesabı bakiyeden mi anlık özkaynaktan mı yaptığınızı, yüzde mi para cinsinden mi verdiğinizi daima belirtin
  3. Seans ısı haritası — saat ve haftanın gününe göre ayrılmış sonuç. Önce saat diliminin doğru belirlenmesini gerektirir; aksi halde var olmayan saatleri tanımlar
  4. Getiri dağılımı — tek tek işlem sonuçlarının histogramı. Tek bir risk ölçütünün göstermediği kalın kuyrukları ve asimetriyi ortaya çıkarır
Yüzde eksi on düşüş içeren sentetik özkaynak ve o ana kadarki zirve grafiği
Düşüş, sermaye akışları açıkça ele alınarak o ana kadarki zirveden ölçülür.

DataFrame'den doğrudan mum grafiği için mplfinance, matplotlib üzerinde çalışan bir katmandır. Bölüm 2'deki kuraldan doğrudan kaynaklanan bir çekince vardır: son sürümü Ağustos 2023 tarihlidir ve hâlâ ön sürüm numarası taşır (0.12.10b0) [13] [DURUM: 2026-10-08]. Bu, onu baştan uygunsuz kılmaz; ancak şu dosyaya eklemeden önce: requirements.txt, kendi verilerinizde matplotlib ve pandas sürümlerinizle çalışıp çalışmadığını kontrol edin; yeni bir sürüm bunu sizin yerinize yapmaz. Betiği kaydırıcılı ve açılır listeli basit bir gösterge paneline dönüştürmek için ise streamlit, düzenli sürüm yayımlayan bir projedir [13] ve şu komutla açılır: streamlit run dashboard.py. İkisi de zaman kazandırır, ikisi de requirements.txt dosyasına bağımlılık ekler; bilinçli ekleyin.

Pratik kural: belirli bir grafiğin hangi soruyu yanıtladığını tek cümlede söyleyemiyorsanız o grafiğe ihtiyacınız yoktur. Sorusu olmayan grafik süstür; sonuç analizinde süs pahalı olabilir, çünkü verilerin içermediği çıkarımları ima eder.

6. Python'da geriye dönük test: kod ve yöntem sınırı

Kolay kaçırılan sınır buradadır. Python testin uygulanmasından sorumludur: verileri yüklemek, sinyali hesaplamak, sonucu doğru döneme atamak, maliyetleri uygulamak ve ölçütleri hesaplamak. Sorumlu olmadığı şey ise metodolojidir: örneklem bölmesi, denenen varyantların sayısı, gerçekleştirme modeli ve sonucun ne kadar dayanıklı olduğunun değerlendirilmesi. Metodoloji ayrı bir makalenin konusudur: Demo hesaplar, geriye dönük test ve forward test; daha iyi hiçbir kitaplık bunun yerini tutamaz.

pandas tek başına backtest değildir. Sinyal sütununu getiri sütunuyla çarpmak bir sonuç dizisi verir; ancak karar ile gerçekleştirme arasındaki hiçbir şeyi modellemez: giriş anı, giriş fiyatı, spread, komisyon, swap puanları, kısmi gerçekleşmeler ve emir reddi. Fikirleri eleme aşamasında bu kabul edilebilir bir basitleştirme olabilir; yeter ki ona sonuç değil, basitleştirme deyin.

Üç sentetik işlemde sabit ve değişken spread maliyeti
Varsayımlarla USD 3.00 veya USD 8.80; tipik spread iddiası değildir.

Kendi döngünüzün ötesine geçmek isterseniz Python ekosisteminde hazır araçlara yönelik üç yaklaşım vardır. Aşağıdaki tablo bunları performans vaatleriyle değil, çalışma biçimleriyle karşılaştırır. Somut süreler veriye, donanıma ve sürüme bağlıdır; ihtiyacınız varsa kendiniz ölçün ve ortamı kaydedin.

ÖzellikTam olay güdümlü yaklaşımVektörleştirilmiş yaklaşımAsgari olay güdümlü yaklaşım
Örnek kitaplıkbacktradervectorbtbacktesting.py
Hesaplama biçimiMum mum; kendi emir/portföy modeliTüm dizilerde eşzamanlı işlemMum mum; basit model
Basit stratejinin kod miktarıÇok (sınıflar, kalıtım)Sütun düşünüyorsanız azAz
Doğal kullanımPortföy mantığı, çok araç, karmaşık emirÇok varyant tarama, ilk elemeÖğrenmek ve bir fikrin anlamlı olup olmadığını hızla kontrol etmek
Başlıca maliyetDik öğrenme eğrisiZor hata ayıklama; sessiz kaydırma hatası kolayKarmaşık strateji sınırları
Kullanımdan önce denetimSon PyPI sürümünün tarihi, kendi pandas ve NumPy sürümlerinizle uyumluluk, maliyetlerin ve gerçekleşme anının nasıl modellendiği
Üç projenin geliştirme hızı çok farklıdır. Bu, yalnızca meraklıları ilgilendiren bir ayrıntı değildir: pandas ve NumPy sürümlerine yetişemeyen bir çerçeve, er ya da geç eski sürümleri ayrı bir ortamda sabitlemenizi gerektirir. Birini seçmeden önce PyPI sayfasını açıp son sürümün tarihini ve desteklenen Python sürümlerinin aralığını kontrol edin. Bu ölçütlerle bakınca üç projenin, ortak bir tablonun düşündürdüğünden çok daha fazla ayrıştığını görürsünüz: backtrader geçmişte sürümler arasında yıllarca süren boşluklar yaşamıştır; son sürümü Nisan 2023 tarihlidir. Yanında, bakım düzeyleri farklı topluluk çatalları da bulunur; vectorbt hem açık bir proje hem de ayrı geliştirilen ticari bir sürüm olarak bulunur; açık sürüm Temmuz 2026 tarihlidir. Bu nedenle önce okuduğunuz belgelerin hangisini anlattığını belirleyin; backtesting.py geçmişte sürümlere birkaç yıl ara vermiş, fakat bu arayı bitirerek Temmuz 2026'da yeni bir sürüm yayımlamıştır; burada geçmiş zaman kullanılması önemlidir. Üç tarih de paket kayıt sisteminden gelir [13]. Bunların hiçbiri bir kütüphaneyi baştan elemek için yeterli değildir; eleme gerekçesi, çalıştığınız sürümlerle uyumsuz olmasıdır. [DURUM: 2026-08-22]

Son satır bir formalite değildir. Her çerçeve, emri hangi fiyattan ve ne zaman gerçekleştireceğine ilişkin bir varsayım yapar; sonucu çoğu zaman stratejinizin mantığı değil, bu varsayım belirler. Herhangi bir kütüphaneye güvenmeden önce belgelerinde üç sorunun yanıtını bulun: girişi hangi fiyattan gerçekleştiriyor, maliyeti nasıl uyguluyor ve mumlar arasındaki fiyat boşluğunu nasıl işliyor?

Örneklem içi ve örneklem dışı sınırı açıkça gösterilmiş iki sentetik eğri
Varyant seçimini sonraki verilerde değerlendirmeden ayırın; her denemeyi raporlayın.

Kod özgürlüğü aynı zamanda risktir. Platformun yerleşik test aracı bazı kurallar dayatarak sizi belirli hatalardan korur. Kendi kodunuz ise böyle bir koruma sağlamaz: eğitim ve doğrulama verilerinin ayrılıp ayrılmadığını sormaz, sinyalin o anda henüz var olmayan bir fiyatı kullandığını fark etmez, kaç varyant denediğinizi saymaz. Böylece bütün sorumluluk yazara geçer. Sonraki bölümler, kod içinde sessizce neyin bozulabileceğini tam da bu nedenle ele alır.

7. Broker API'leri: kimlik doğrulama, limitler, hatalar, test ortamı

“Broker API sağlıyor” sözü bazen “bir betikten hesaba sınırsız erişebilirim” diye anlaşılır. İşleyiş böyle değildir. API; kimlik doğrulaması, izinleri, istek hızı sınırı ve tanımlı hata kodları olan bir uç noktalar kümesidir. Bazen test ve canlı ortam olarak ayrılır, bazen ayrılmaz; ayrımın nasıl yapıldığı da varsayılacak bir kural değil, belgelerde araştırılacak ayrı bir sorudur. İlk satırı yazmadan önce dokuz soruyu yanıtlayabilmelisiniz: kimlik doğrulaması nasıl yapılır, token hangi izinlere sahiptir, saniyede kaç istek gönderebilirsiniz, test ortamı canlıdan nasıl ayrılır, hangi veri türleri döner, hangi hata kodları tanımlıdır, bölgesel kısıtlar var mıdır, bağlantıyı sürdürme politikası nedir ve token'ın emir gönderme hakkı var mıdır?

MT5 IPC, TWS/IB Gateway ve doğrudan REST mimarileri
Kitaplık gerekli terminal veya aracı uygulamayı kaldırmaz.

Limitler somuttur ve yayımlanır; yine de hangi tür limiti okuduğunuzu ayırt etmelisiniz. Çoğu rehber bu ayrımı atlar; oysa döngüyü nasıl tasarlayacağınızı belirleyen budur. OANDA v20'de iki sayı iki ayrı belgede yer alır ve farklı şeyler ifade eder. Geliştirici rehberi, fiilen uygulanan değeri verir: REST API için “Saniyede 120 istek. Fazla istekler HTTP 429 hatası alır.” Buna yirmi etkin akış ve saniyede iki yeni bağlantı sınırı eşlik eder; hepsi istekleri gönderen IP adresi için sayılır [15]. En iyi uygulamalar belgesi ise bir öneri sunar: sağlayıcı, mevcut bir bağlantıda saniyede yüz istekle ve saniyede iki yeni bağlantıyla sınırlı kalmanızı tavsiye eder [5]. [DURUM: 2026-08-22]

Dolayısıyla saniyede yüz, sağlayıcının altında kalmanızı önerdiği eşik; yüz yirmi ise sunucunun aşıldığında 429 hatası verdiği eşiktir. “100/s azami değerdir” varsayımıyla kendini sınırlayan kod zarar vermez. Fakat bu sayıyı kesin sınır sayıp 429'u işlemeyen kod ilk aşımda çöker; çünkü uygulanan sınır ile öneri aynı yerde değildir. Bunlar tek sağlayıcının sayılarıdır, piyasa kuralı değildir; başka bir brokerda farklıdır ve [ÖZELLİK BROKERA/HESABA BAĞLI]. Sorgulama döngüsü yazmadan önce kendi sağlayıcınızın belgelerinde kontrol edin.

Yaygın rehberlere ve bu paragrafın önceki sürümüne ilişkin bir düzeltme. Birçok kaynak OANDA için “saniyede yaklaşık 30 istek” sayısını tekrarlar. Bu bilgi eskidir. Ancak daha yaygın ve fark edilmesi daha zor hata, önerilen saniyede 100 isteği azami sınır olarak vermektir. Bu makale de düzeltilmeden önce aynı hatayı yapıyordu. Öneri ile uygulanan sınır, aynı sağlayıcının iki ayrı belgesinde yer alan iki farklı büyüklüktür [5] [15]. Bunları karıştırarak tasarlanan kod ya kapasiteyi boşa harcar ya da hazırlıklı olmadığı bir engellemeyle karşılaşır. Limitleri öğretici bir yazıdan değil, belgelerden doğrulayın; okuduğunuz sayının tavsiye mi yoksa kesme eşiği mi olduğunu kontrol edin.

Test ortamı farklı mod değil farklı ana makinedir. OANDA ayrımı adreste gösterir: api-fxpractice.oanda.com test ortamı için, diğeri api-fxtrade.oanda.com canlı ortam içindir; veri akışının da benzer bir adres çifti vardır [15] [DURUM: 2026-08-22]. Bu iyi bir özelliktir; çünkü hatayı kodda yakalayabilir, canlı ortamın ana makinesini ek bir koşulla engelleyebilirsiniz. Sağlayıcı tek adres kullanıp ortamları yalnızca token ile ayırıyorsa durum daha zordur: tek engel yapılandırmanız olur.

Python'u hesaba bağlamanın dört yaygın yolu vardır; fark, “gelişmişlik” düzeyinde değil, mimaridedir:

ÇözümBağlantının gerçek yeriKoşullarNotlar
OANDA v20 (REST + akış)Betik → broker sunucuları, doğrudan HTTPO brokerda hesap; test ve canlı ortamlar için ayrı ana makinelerHerkese açık belgeler ve açıkça belirtilmiş limitler. Bu bir sektör standardı değil, tek sağlayıcının API'sidir
Interactive Brokers (TWS / IB Gateway)Betik → yerel aracı uygulama → broker sunucularıÇalışan TWS veya IB Gateway gerekir; asenkron modelAracı uygulama kapatıldığında betiğin bağlantısı kesilir. Sarmalayıcı kütüphaneler kullanımı kolaylaştırır, fakat bu bağımlılığı ortadan kaldırmaz. İsme dikkat edin: sıkça anılan ib_insync Temmuz 2023'ten beri yeni sürüm almamıştır ve eski bir TWS API sürümüne dayanır; etkin biçimde sürdürülen devam projesi ise ib_async [13] [DURUM: 2026-10-08]
Resmî MetaTrader5 paketiBetik → aynı makinede kurulu MT5 terminali → broker sunucusuMT5 terminali aynı makineye kurulmuş olmalıdır; initialize() çağrısı, terminal çalışmıyorsa onu başlatır; terminalde saklanan veya çağrıya aktarılan giriş bilgilerini kullanır [18]; entegrasyon build 2085'ten (2019) beri vardır [7]Bu, bağımsız bir broker API'si değil, terminalle kurulan iletişimdir [6]. Terminale bağımlılık bir kusur değil, mimarinin özelliğidir. Resmî PyPI paketi yalnızca Windows'a kurulur. macOS ve Linux'ta terminali Python ile birlikte Wine altında çalıştırmak ya da ayrı bir aracı katman kullanmak gerekir [ÖZELLİK SÜRÜME/ORTAMA BAĞLI]
Harici bulut köprüsü (ör. MetaApi)Betik → harici hizmet → sağlayıcının bulutundaki terminal → broker sunucusuHesap erişimini üçüncü tarafa vermeyi gerektirir; fiyatlandırma ve limitler plana bağlıdırZincire bir halka ve güvenmeniz gereken bir taraf ekler. Emir gönderecek köprü yatırımcı parolasına değil, ana parolaya ihtiyaç duyar: yalnızca okuma değil, hesap üzerinde işlem yapma yetkisi ister. Fiyatlandırmayı sağlayıcıdan kontrol edin [ÖZELLİK PLANA BAĞLI]
Yatırımcı parolası ile ana parola, birbirinden farklı iki erişim kararıdır. MetaTrader broker hesabının iki parolası vardır. Yatırımcı parolası salt okunur erişim sağlar: pozisyonları ve geçmişi görürsünüz, fakat emir gönderemez veya değiştiremezsiniz. Ana parola hesabın tam kontrolünü sağlar. Harici hizmetlere, örneğin günlüğe, sonuç doğrulayıcıya veya istatistik panosuna en fazla yatırımcı parolasını verirsiniz. Emir gönderecek bir bulut köprüsü ise tanımı gereği yatırımcı parolasıyla çalışamaz; ana parolayı gerektirir. Bu nedenle onu bağlamak, günlük bağlamaktan nitelikçe farklı bir karardır. Belirli sağlayıcının gerçekte hangi erişimi istediğini güncel belgelerinden kontrol edin. Ana parolayı zaten verdiyseniz ve bu erişimin sürmesini istemiyorsanız, terminalden veya broker panelinden değiştirerek üçüncü taraf erişimini geçersiz kılın. Emir geçmişinde tanımadığınız işlemleri arayın ve brokerın sunduğu ek kimlik doğrulamasını etkinleştirin. Salt okunur erişimin bile tarzınızı, çalışma saatlerinizi ve pozisyon büyüklüklerinizi açığa çıkardığını unutmayın; bu bir formalite değil, gizlilik kararıdır.

Döviz piyasası dışında şu kütüphaneyi de tanımakta yarar vardır: ccxt kripto para borsalarına erişimi tek arayüzde birleştirir. Bireysel bir brokerdaki FX/CFD hesabına uygulanmaz; buna rağmen bazen evrensel çözüm sanılır.

cTrader. Platformunuz cTrader ise algoritmalar cTrader Algo ortamında yazılır. Bu ortam, cBots, göstergeler ve eklentiler dâhil olmak üzere hem C# hem Python'u yerleşik olarak destekler [8] [DURUM: 2026-08-22]. Bu, yukarıdaki yapılardan farklı bir modeldir: kod API'ye bağlanan ayrı bir süreçte değil, platformun içinde çalışır. Ürün etkin biçimde geliştirildiği için işlevlerin kapsamını ve nasıl başlatıldığını sağlayıcının güncel belgelerinden kontrol edin.

Anahtar ve parolalar: kodda asla

Betiğin içine yazılan API anahtarı onunla birlikte depoya, yedeğe ve sürüm geçmişine taşınır; satırı silmek anahtarı Git geçmişinden kaldırmaz. Herkese açık depolar bu tür dizeler için otomatik taranır. Bir sırrın bulunabileceği yerler yalnızca ortam değişkeni, depo dışında tutulan ve .gitignore içinde listelenen yapılandırma dosyası veya değeri bu yollardan biriyle sağlayan bir sır yöneticisidir.

"""Yapılandırma ve sırlar: hassas hiçbir şey kod dosyasına girmez."""
import os
from dataclasses import dataclass

HOSTS = {
    "practice": "https://api-fxpractice.oanda.com",
    "live": "https://api-fxtrade.oanda.com",
}


class MissingConfiguration(RuntimeError):
    """Başlamak için zorunlu veri eksikse üretilir."""


@dataclass(frozen=True)
class Configuration:
    token: str
    account: str
    environment: str

    @property
    def host(self) -> str:
        return HOSTS[self.environment]


def _required(name: str) -> str:
    value = os.environ.get(name, "").strip()
    if not value:
        # Varsayılanın yokluğu bilinçlidir: boş token isteği programı durdurmak yerine
        # yetkilendirmesiz gönderirdi.
        raise MissingConfiguration(f"Eksik ortam değişkeni: {name}")
    return value


def load_config() -> Configuration:
    environment = os.environ.get("BROKER_ENV", "practice").strip().lower()
    if environment not in HOSTS:
        raise MissingConfiguration(f"BROKER_ENV şunlardan biri olmalı: {sorted(HOSTS)}")
    return Configuration(
        token=_required("BROKER_API_TOKEN"),
        account=_required("BROKER_ACCOUNT_ID"),
        environment=environment,
    )

Bu parçada üç bilinçli karar vardır. Varsayılan ortam canlı değil test ortamıdır; böylece yapılandırma hatası gerçek hesap yerine demo hesaba götürür. Eksik ortam değişkeni boş dizeyle geçiştirilmez, programı durdurur. Token'ın hiçbir yerde varsayılan değeri yoktur; yorum satırında bile bulunmaz, çünkü yoruma alınmış sır da ifşa edilmiş sırdır.

Önceki python-dotenv bu kodun yerini almaz; ondan önce çalışır. load_dotenv() çağrısı (önce from dotenv import load_dotenv içe aktarıldıktan sonra), depo dışında tutulan .env dosyasını sürecin ortam değişkenlerine yükler; ancak bundan sonra load_config() bu değerleri okur. Böylece yapılandırma işlevi değerin dosyadan mı, sistem ayarından mı, sır yöneticisinden mi geldiğini bilmez ve bilmesi gerekmez. .env dosyasının kendisi de bütün yapılandırma diziniyle birlikte .gitignore içine alınır.

Veri çekme: zaman aşımı, yineleme, yanıt kodları

Zaman sınırı olmayan istek süresiz takılarak bütün süreci dondurabilir. Sınırsız yinelenen istek ise kotayı tüketip erişiminizin kesilmesine neden olabilir. Aşağıdaki parça, veri okumak için asgari korumaları ve daha sonra hesabı koruyacak bir ayrımı gösterir: yalnızca yinelendiğinde broker tarafındaki durumu değiştirmeyen, yani idempotent işlemler tekrar denenebilir.

import logging
import time

import requests

TIMEOUT_S = 10.0        # bağlantı/okuma hareketsizlik saniyesi; toplam iş süresi değil
MAX_ATTEMPTS = 4            # ilk dâhil deneme sayısı
BACKOFF_S = 1.0         # üstel gecikme tabanı, saniye

log = logging.getLogger("data")


class ApiError(RuntimeError):
    """Yinelemeyle düzeltilemeyen hata."""


def fetch_candles(session: requests.Session, host: str, token: str,
                   instrument: str, parameters: dict) -> dict:
    """Yalnızca broker durumunu değiştirmeyen GET yinelenir."""
    url = f"{host}/v3/instruments/{instrument}/candles"
    headers = {"Authorization": f"Bearer {token}"}

    for attempt in range(1, MAX_ATTEMPTS + 1):
        delay = BACKOFF_S * 2 ** (attempt - 1)
        try:
            response = session.get(url, headers=headers, params=parameters, timeout=TIMEOUT_S)
        except requests.RequestException as error:
            if attempt == MAX_ATTEMPTS:
                raise ApiError(f"Bağlantı yok: {MAX_ATTEMPTS} denemeden sonra") from error
            log.warning("deneme %s: %s", attempt, error)
            time.sleep(delay)
            continue

        if response.status_code == 429:
            # Sıklık limiti aşıldı. Broker döndürürse Retry-After başlığı,
            # kendi tahmininizden daha güvenilirdir.
            try:
                wait = float(response.headers.get("Retry-After", delay))
            except (TypeError, ValueError):
                # Belirtim saniye yerine HTTP tarihine de izin verir;
                # bu dal yoksa float() tüm indirmeyi sonlandırır.
                wait = delay
            log.warning("sıklık limiti, %.1f s bekleniyor", wait)
            time.sleep(wait)
            continue

        if 500 <= response.status_code < 600:
            log.warning("sunucu hatası %s, yineleniyor", response.status_code)
            time.sleep(delay)
            continue

        if response.status_code != 200:
            raise ApiError(f"HTTP {response.status_code}: {response.text[:200]}")

        try:
            data = response.json()
        except ValueError as error:
            raise ApiError("Geçersiz JSON yanıtı") from error
        if not isinstance(data, dict):
            raise ApiError("JSON yanıtı nesne olmalı")
        if not isinstance(data.get("candles"), list) or not data["candles"]:
            raise ApiError("Yanıtta mum yok; araç ve tarih aralığını kontrol edin")
        return data

    raise ApiError(f"Geçerli yanıt yok: {MAX_ATTEMPTS} denemeden sonra")

Doğrulama: broker bağlantısı olmadan çevrimdışı sınandı. Yayımlanan fetch_candles() işlevi gerçek sunucu yerine bellekte HTTP oturumu taklidiyle çalıştırıldı. Dokuz senaryonun ilkinde 429 için Retry-After saniye değeri, ardından 503 ve başarı tek dizidedir; diğerleri boş mum listesi, 404, HTTP-tarih başlığı, deneme limiti, bağlantı istisnası, geçersiz JSON, nesne olmayan yanıt ve geçersiz candles türüdür. sleep() çağrıları, gerçekten beklemek yerine istenen gecikmenin kaydıyla değiştirildi. Test soket açmaz ve ağ zamanlamasını ölçmez. Ortam: Python 3.12.10, requests 2.34.2, Windows 11, 2026-10-08 tarihinde kontrol edildi. Gerçek senaryo raporu:

A: 429 saniye -> 503 -> 200: çağrı=3; bekleme=[3.0, 2.0]; tek mum
B: boş mumlar: çağrı=1; bekleme=[]; beklenen hata
C: HTTP 404: çağrı=1; bekleme=[]; beklenen hata
D: HTTP-tarih yedeği: çağrı=2; bekleme=[1.0]; tek mum
E: deneme limiti: çağrı=4; bekleme=[0.0, 0.0, 0.0, 0.0]; beklenen hata
F: taşıma hatası: çağrı=4; bekleme=[1.0, 2.0, 4.0]; beklenen hata
G: geçersiz JSON: çağrı=1; bekleme=[]; beklenen hata
H: nesne olmayan JSON: çağrı=1; bekleme=[]; beklenen hata
I: yanlış mum türü: çağrı=1; bekleme=[]; beklenen hata

Bu sonucun doğruladıkları ve doğrulamadıkları. Sonuç, önceden hazırlanmış dokuz senaryodaki beklenen davranışı doğrular. A, 429 yanıtında kodun bir saniyesi yerine sunucunun üç saniyelik beklemesinin seçildiğini denetler. D, float() bir HTTP tarihini ayrıştıramadığında kullanılan yedek yolu denetler. F ise timeout=10 değerinin aktarıldığını, dört deneme yapıldığını ve taklit edilen taşıma arızalarının istisnayla sonlandığını kontrol eder. Bu, gerçek bağlantının toplam bitiş süresini kanıtlamaz: requests zaman aşımı bütün işin son süresi değildir ve burada bekleme taklit edilmiştir. B, C ve G–I boş veya geçersiz yanıtların reddini sınar. Test, belirli bir brokerın API erişilebilirliğini, her veri alanını veya ağ davranışını doğrulamaz.

Bununla birlikte brokerın kendisi hakkında hiçbir şeyi doğrulamaz. Uç noktanın yolu, parametre adları, yanıtın yapısı ve sağlayıcının Retry-After başlığını döndürüp döndürmediği, belirli bir API'nin özellikleridir [ÖZELLİK BROKERA/HESABA BAĞLI] ve kendi belgelerinde kontrol edilip test hesabında sınanmalıdır. Taklit, kodunuzun doğruluğunu gösterir; başka birinin sunucusuna ilişkin varsayımlarınızın doğruluğunu değil. Testin bütün değeri bu ayrımdır.

Kotasyon, istek ve alım saatleri ayrı sorgu/akış karşılaştırması
Hızlı mesaj alımı, veri tazeliği veya emir süresi kanıtlamaz.

Gecikme aktarılmaz, ölçülür. Rehberlerde dolaşan “bu API 50–200 ms, diğeri 100–500 ms” aralıkları; istemcinin konumunu, bağlantısını, günün saatini ve ölçüm yöntemini belirtmedikleri için değersizdir. Başkasının sayısını kopyalamak yerine ölçtüğünüz büyüklükleri ayırın: veri uç noktasının yanıt süresi, emir gönderiminden kabul onayına kadar geçen süre, gerçekleşme onayına kadar geçen süre ve karar anındaki piyasa verisinin yaşı. Bunlar dört ayrı büyüklüktür; yalnızca biri gündelik dilde “gecikme” diye adlandırılır. Her birini kendi zincirinizde ölçün, günlüğe kaydedin ve tarih, broker ve deneme sayısıyla birlikte verin. ICMP ping bu dört değerin hiçbiri değildir.

Sorgulama mı akış mı? Periyodik REST sorgulamasında veri yaşı istekler arasındaki aralığa bağlıdır. Her beş saniyede bir sorgulamak, en kötü durumda beş saniye önceki fiyatla karar vermek demektir; daha sık sorgulama kotayı tüketir. API'ye göre WebSocket veya HTTP akışı bunun tersini yapar: güncelleme oluşunca sunucu gönderir. Mum içinde tepki vermek önemliyse veya sorgulama kotayı tüketecekse anlamlıdır. Sonraki H1 mumunun açılışında giriş yapan strateji için normal sorgulama yeterlidir ve bakımı daha kolaydır. Akış ise sorgulamada bulunmayan üç sorun ekler. İlki yeniden bağlantı boşluğu: bağlantı otuz saniye kesildiğinde soket o otuz saniyeyi yeniden göndermez. Seriye tamam demeden önce eksik parçayı ayrı bir geçmiş veri isteğiyle almalısınız. İkincisi ölü bağlantı tespiti: veri göndermeyi kesen soket, kod açısından hiçbir hareket olmayan piyasayla aynı görünür. Bu yüzden heartbeat veya keep-alive gibi bir varlık sinyali ve kendi sessizlik sayacınız gerekir; süre dolunca bağlantıyı kopmuş sayıp yeniden kurarsınız. Üçüncüsü sıralama ve mükerrerler: bağlantı geri geldiğinde daha önce işlediğiniz mesajı yeniden alabilirsiniz. Bu, emirlerdeki sorunla aynıdır; olay kimliği ve daha önce kaydedilip kaydedilmediğini kontrol ederek çözülür.

Veri ve emrin limitleri ayrı. Ciddi bir API fiyat akışını işlem akışından ayırır; hesap durumunu değiştiren işlemlerin limitleri, okuma limitlerinden çok daha düşük olabilir. Hareketli piyasada stop-loss'u saniyede bir düzine kez değiştirmeye çalışan kodun tam da işlem yolundaki erişimi kesilebilir. Böylece açık pozisyonunuz kalır, fakat geçici olarak ona programla erişemezsiniz. Bu yüzden brokerın hoşgörüsüne güvenmek yerine değişiklik hızını kendi tarafınızda sınırlamalısınız.

Otomasyon gerçekleşme koşulunu değiştirmez. Emri insan da betik de gönderse brokerın emir gerçekleştirme modeli, akış işleme yöntemi ve veri kaynağının kalitesi sonucu etkiler. Giriş maliyetine duyarlı stratejilerde sonucu belirleyebilirler. Bunları kursun broker seçimi bölümündeki gerçekleşme modelleri ve gerçekleşme kalitesi yazıları ele alır. Kod açısından tek sonuç yeterlidir: API, gerçekleştirme isteğinin gönderimini hızlandırır; olmayan likiditeyi yaratmaz. Bu, sert hareket günlerinde özellikle belirgindir. İsviçre Merkez Bankası 15 Ocak 2015'te euro başına 1.20 franklık asgari kuru kaldırmış ve aynı anda faizi yüzde −0.75'e indirmiştir [17]. O gün işleyen mekanizma geneldir; herhangi bir anekdota inanmayı gerektirmez. Koruyucu emir seviyesinde karşı likidite yoksa emir yazılan fiyattan değil, ilk mevcut fiyattan gerçekleşir. Bu fiyatın uzaklığını bağlantınız değil, emir defteri belirler. O dönemde belirli brokerlarda yaşananların ölçeği broker seçimi bölümünde anlatılır. Kod için sonuç şudur: gönderim hızı, kimsenin işlem yapmak istemediği fiyatı yaratmaz.

8. Notebook'tan hizmete: emir kodu betikten nasıl ayrılır?

Makalenin asıl sınırı burasıdır. Günlük istatistiklerini hesaplayan notebook yarıda çökebilir; bir şey olmaz, yeniden çalıştırırsınız. Hesaba müdahale edebilen kod ise aynı hatadan sonra geride durum bırakır: açık pozisyon, kısmen değiştirilmiş emir veya çift giriş. Fark kod kalitesinde değil, bunlardan birinin geri alınamayan sonuçlar doğurmasındadır.

Araştırma notebook'u ≠ emir programı. Herhangi bir koda emir gönderme yetkisi vermeden önce şu kümenin tamamı bulunmalıdır: yapılandırma koddan ayrılmış olmalı · sırlar depo dışında tutulmalı · günlük her kararı ve her sunucu yanıtını kaydetmeli · testler mantığı ağ bağlantısı olmadan sınamalı · yinelemeler sınırlı sayıda ve yalnızca idempotent işlemler için yapılmalı · zaman aşımları her ağ çağrısında · durum süreç yeniden başlatıldığında da korunmalı · izleme bir varlık sinyali içermeli · acil durdurma yeni girişleri durdurmalı · devreye alma prosedürü yeni sürümün nasıl devreye alınacağını açıklamalıdır. Tek bir öğenin eksikliği bile elinizde emir gerçekleştirme programı değil, prototip olduğunu gösterir. Testin özkaynak eğrisi ne kadar iyi görünürse görünsün ona böyle davranmalısınız.

Üç otomasyon düzeyi — fark teknolojide değil, son kararı kimin verdiğindedir:

Düzey 1: bildirim. Betik koşulları kontrol edip mesaj gönderir. Hesaba erişimi yoktur; emir göndermeye yetkili token gerekmez. Kararı ve gerçekleştirmeyi insan üstlenir. Kod hatasının yalnızca rahatsızlık yarattığı tek düzey budur.

Düzey 2: onaylı hazırlık. Betik emri, yani enstrümanı, yönü ve büyüklüğü hazırlar; operatörün açık onayını bekler ve ancak sonra gönderir. Böylece hesap hataları pozisyona dönüşmeden yakalanır ve insan karar döngüsünde kalır. Yazılım bakımı yapmayı düşünmeyen bireysel yatırımcı için bu çoğu zaman geçiş aşaması değil, varış noktasıdır. Ancak bu bir araştırma bulgusu değil, editoryal değerlendirmedir.

Düzey 3: onaysız çalışma. Betik pozisyonları kendi başına açıp kapatır. Bunun için yukarıdaki kutunun tamamı, önce test hesabında sonra asgari pozisyon büyüklüğüne sahip hesapta deneme ve ilk çalışma döneminde gözetim gerekir. Sonraki aşamaya geçmeden kaç işlemin yeterli olduğu, yuvarlak bir sayıya değil örneklem büyüklüğüne ve istatistiksel güce ilişkin sorudur; bu konu test makalesinde ele alınır.

Çevrimdışı analiz, kapı ve emir adaptöründen ayrılmıştır
Önce analitik doğruluk; gönderim ayrı tasarım, test ve durum onayı ister.

Güvenlik kapılı emir katmanı

Emir gönderen kodu gösteriyorsanız onu neyin durdurduğunu da göstermelisiniz. Aşağıdaki modül bütünüyle çalıştırılabilir ve tek bir ağ çağrısı içermez: emri kurar, limitleri denetler ve kapılarda durur. Gönderim ise ayrı bir parçadır ve bilerek kod olarak verilmemiştir.

"""Emir katmanı: doğrulama, test ve canlı öncesi açık kapı."""
import logging
import os
import sys
import time
import uuid

ALLOWED_INSTRUMENTS = {"EUR_USD"}
MAX_UNITS = 1_000          # lot değil baz döviz birimi; kural API'ye bağlıdır

# Canlı onay burada olmadığı için dosyadan okunamaz.
# Program ortam değişkeninden alır; doğru değer olarak
# operatörün işlem yapmak istediği hesap numarasını kabul eder; kabuğa elle
# başlamadan önce girilir. Sınırı çıktı altındaki kutu anlatır.
CONFIRMATION_VARIABLE = "BROKER_LIVE_CONFIRMATION"

# Bu ayar yoksa log.info yazılmaz; kök varsayılan
# WARNING ve hedef yoktur. Dosya arıza izi bırakır;
# akış aynısını canlı gösterir. Büyük programda günlük ayarı
# modüle değil giriş noktasına aittir.
# Denetim zaman damgalarını açık Z işaretiyle UTC biçiminde yaz.
logging.Formatter.converter = time.gmtime
logging.basicConfig(
    level=logging.INFO,
    format="%(asctime)s.%(msecs)03dZ %(levelname)s %(name)s %(message)s",
    datefmt="%Y-%m-%dT%H:%M:%S",
    handlers=[logging.FileHandler("execution.log", encoding="utf-8"),
              logging.StreamHandler(sys.stdout)],
)

log = logging.getLogger("execution")


class SafetyGateError(RuntimeError):
    """Emri süreçten çıkmadan durdurur."""


def new_order_id() -> str:
    """Her emir niyetinde BİR KEZ çağrılır, gönderimden önce saklanır.

    Aynı emrin her denemesinde aynı değer dönmelidir;
    yoksa mükerrer tanınamaz.
    """
    return str(uuid.uuid4())


def build_order(instrument: str, units: int, order_id: str) -> dict:
    if instrument not in ALLOWED_INSTRUMENTS:
        raise SafetyGateError(f"İzinli olmayan araç: {instrument}")
    # bool, int alt sınıfı; isinstance(True, int) doğrudur. Açıkça
    # reddedilmezse mantıksal değer doğrulamayı geçip veriye girer.
    if isinstance(units, bool) or not isinstance(units, int) or units == 0:
        raise SafetyGateError("Büyüklük sıfırdan farklı tamsayı olmalı")
    if abs(units) > MAX_UNITS:
        raise SafetyGateError(
            f"Büyüklük {abs(units)} şu limiti aşıyor: {MAX_UNITS}")
    return {
        "order": {
            "type": "MARKET",
            "instrument": instrument,
            "units": str(units),
            # Kimlik dışarıdan gelir, denemeler arası değişmez.
            # Burada üretilse her yineleme farklı değer taşırdı.
            # Alan adı ve sınırlar ilgili API'ye aittir.
            "clientExtensions": {"id": order_id},
        }
    }


def _operator_confirmation() -> str:
    """Dosya dışından okur; kod değeri bilmez ve içermez."""
    return os.environ.get(CONFIRMATION_VARIABLE, "").strip()


def submit_order(session, cfg, instrument: str, units: int, order_id: str,
                  test_mode: bool = True) -> dict | None:
    """Varsayılan hiçbir şey göndermez. Canlı ana makine iki karar ister."""
    order = build_order(instrument, units, order_id)

    if test_mode:
        log.info("TEST MODU [%s] emir hazır, gönderilmedi: %s",
                 cfg.environment, order)
        return None

    if cfg.environment == "live" and _operator_confirmation() != cfg.account:
        raise SafetyGateError(
            "Canlı ana makine açık operatör onayı ister")

    # Kapılar geçildi, modül burada biter; gönderim içermez,
    # çünkü dürüst çalıştırılamaz; yapı alttaki taslakta anlatılır.
    raise NotImplementedError(
        "Bu modülde gönderim yok; yapısal taslağa bakın")

Emir kimliği deneme başına değil bir kez oluşturulur. Bağlantı koptuktan sonra mükerrer isteği tanıyabilmek için bu değer emir verme niyeti oluştuğunda hesaplanmalı, gönderimden önce kalıcı kayda alınmalı ve aynı emrin sonraki bütün denemelerinde yeniden kullanılmalıdır. Eğer uuid.uuid4() emir oluşturmanın içinde olsaydı her yeniden deneme farklı değer gönderirdi. İkinci isteği ilkinin tekrarı saymak için dayanak kalmazdı. Bu nedenle build_order() kimliği kendisi üretmek yerine argüman olarak alır.

Bu kimliğin çözmediği şey. Yukarıdaki clientExtensions alanı, tek sağlayıcının çözümüdür; sektör standardı veya belgelenmiş idempotentlik garantisi değildir. OANDA belgeleri bunu emre eklenen isteğe bağlı özel veri olarak tanımlar. Ayrıca “Hesabınız MT4 ile bağlantılıysa tradeClientExtensions ayarlamayın, değiştirmeyin veya silmeyin” uyarısını yapar [14] (kontrol tarihi 2026-08-22). Aynı kimlikle yinelenen piyasa emrinin mükerrer diye reddedileceği sözü verilmez. Piyasa emri hemen gerçekleştiğinden tekrarı tanıma aralığı çok kısadır. Bu yüzden mükerrer tanıma, gönderilen verideki alana değil sizin tarafınızdaki iki unsura dayanır: emir niyetinin gönderimden önce kalıcı olarak kaydedilmesi ve arıza sonrasında brokerdan alınan emir ve işlem listesiyle uzlaştırılması. Alanın adını ve kısıtlarını kendi API'nizin belgelerinden kontrol edin; başka sağlayıcıda adı farklı olabilir veya hiç bulunmayabilir.

Emir gönderme: yapısal taslak

Aşağıda kopyalanacak bir işlev yoktur. Kendi kodunuza aktarabileceğiniz, fakat yapıştırıp çalıştıramayacağınız adım sırası ve işlem adlarından oluşan yapısal taslak vardır. Bu biçim bir eksiklik değil, tercihtir. Editörler bu dalı çalıştırmadı ve çalıştırmayı amaçlamıyor: emir göndermek broker hesabı gerektirir; başkasının hesabında işlem kodu denemek test değil, onun üstlendiği risktir. Tam bir işlev burada hiç çalıştırılmamış bir ürün olur ve öncelikle doğrulanmamış koda güvenmeyi öğretirdi. Aynı tercih MQL taslaklarında da yapıldı: makale 11.5 ve Pine Script taslağında makale 11.2.

YAPISAL TASLAK - emir gönderme ve eksik yanıtı ele alma
HTTP katmanı için yön gösteren kayıt. Tam, çalıştırılabilir kod değildir.

bildirim: StanNieznany - yanıt gelmedi; emrin varlığı bilinmiyor.
          Ayrı tür; ağ hatasının alt sınıfı değil. Ağ hatası yinelenebilir,
          bu durum asla yinelenmez.

girdi: doğrulanmış emir, order_id, yapılandırma, HTTP oturumu
çıktı: broker yanıtı veya StanNieznany; asla sessiz başarı

1. KALICI KAYIT - her şeyden önce
   süreç belleği dışına yaz: order_id, UTC zamanı,
   instrument, units, environment, status := INTENT
   ilerlemeden diske yazmayı zorla
   yoksa yeniden başlatma niyeti kaybettirir;
   program ne denediğini bilemez

2. GÜNLÜK - gönderimden önce
   order_id + environment + emir içeriğini kaydet
   token asla günlüğe girmez
   hesap numarasının son dört karakteri dışında maskele
   günlüğü sonradan insan; adım 1 kaydını program okur

3. TEK İSTEK, SIFIR YİNELEME
   API belgesindeki emir yoluna POST
   yetki başlığına ayardaki token; koddan asla
   zaman aşımı zorunlu; yoksa çağrı sonsuza asılabilir

4. YANIT GELMEDİ (zaman aşımı veya taşıma hatası)
   YİNELEME - POST idempotent değildir
   kayıtta status := UNKNOWN
   StanNieznany üret
   DURUM UZLAŞTIRMA: brokerdan son birkaç dakikanın emir ve işlemlerini sorgula
   order_id ara; emrin varlığını belirledikten sonra yeni karar ver
   (bu prosedür burada yok; taslak altındaki paragrafa bak)

5. YANIT GELDİ
   HTTP kodu + kırpılmış yanıt gövdesini kaydet
   kod (200, 201) dışında -> status := REJECTED -> hata üret
   gövde JSON değilse    -> status := UNKNOWN -> StanNieznany üret
   aksi hâlde           -> status := ACCEPTED -> yanıtı döndür

taslağın içermediği ve içeremeyeceği:
   hesaba yapıştırılıp çalıştırılacak hazır işlev yok;
   koruma alanının adı da yok: başka örnekten değil,
   ilgili API belgesinden gelir.

Taslağın yapmadıkları — bu da anlattıkları kadar önemlidir. Taslak koruyucu seviye eklemez: ne stop-loss ne take-profit bulunur. Bu bir ihmal değildir; ayrı alan, mesafe veya mutlak fiyat ve kendi geçerlilik süresi gibi kurallar API'ler arasında değişir. Bu parça başkasının örneğinden değil, kendi brokerınızın belgelerinden gelmelidir. Pratik sonuç tam olarak 4. adımla ilgilidir: StanNieznany sonrasında korumasız bir açık pozisyonunuz olabilir ve var olup olmadığından bile emin olmayabilirsiniz. Emir gönderebilen kodda koruyucu seviye girişle aynı istekte gider; yoksa pozisyonun stopsuz kaldığı bir zaman aralığı oluşur.

Eksik olan yalnızca koruyucu seviyeler değildir. “Araştırma notebook'u ≠ emir programı” kutusundaki izleme, acil durdurma ve devreye alma prosedürü henüz ne çalıştırılabilir modülde ne taslakta bulunur. Yeniden başlatmayı aşan durum, 1. adımda yalnızca gereksinim olarak vardır, uygulama olarak değil. Günlükler, durum ve testler bölümü bunları anlatır; kodla göstermez. Sonuç kutudakiyle aynıdır: bu, mimari öğrenme malzemesidir, emir gerçekleştirme programı değildir.

Güvenlik kapıları çevrimdışı sınandı — ağ, broker ve sır olmadan, taklit yapılandırmayla. Aşağıda yalnızca çıktı değil, testin tamamı bulunur. Aksi hâlde sözüme güvenmek zorunda kalırdınız; makalenin uyardığı güven tam da budur.

"""Ağsız kapı testi: taklit ayar, sır ve POST yok."""
from dataclasses import dataclass


@dataclass(frozen=True)
class MockConfiguration:
    environment: str = "practice"
    host: str = "https://api-fxpractice.oanda.com"
    token: str = "dummy-token-for-offline-test"
    account: str = "000-000-0000000-000"


def test_gates() -> None:
    cfg = MockConfiguration()
    order_id = new_order_id()
    print("yapılandırma:", cfg.environment, cfg.host, "token_length=", len(cfg.token))

    # session=None bilinçlidir: kod POST denese,
    # test sessiz geçmek yerine AttributeError ile çökerdi.
    result = submit_order(None, cfg, "EUR_USD", 100, order_id)
    assert result is None
    print("1. test modu: gönderilmedi - OK")

    for description, instrument, units in [
        ("izinli olmayan araç", "GBP_JPY", 100),
        ("limit üstü büyüklük", "EUR_USD", 5000),
        ("sıfır büyüklük", "EUR_USD", 0),
        ("sayı yerine mantıksal değer", "EUR_USD", True),
    ]:
        try:
            build_order(instrument, units, order_id)
        except SafetyGateError as error:
            print(f"2. kapı [{description}]:", error)
        else:
            raise AssertionError(f"Beklenen kapı tetiklenmedi: {description}")

    cfg_live = MockConfiguration(environment="live", host="https://api-fxtrade.oanda.com")
    try:
        submit_order(None, cfg_live, "EUR_USD", 100, order_id, test_mode=False)
    except SafetyGateError as error:
        print("3. kapı [onaysız canlı]:", error)
    else:
        raise AssertionError("Canlı mod onaysız geçti")

    print("tüm kapılar sınandı")


if __name__ == "__main__":
    test_gates()

Çalıştırma çıktısı — Python 3.12.10, bölüm 2 ile aynı ortamda, ağ bağlantısı ve standart kitaplık dışı paket olmadan: gönderim çıkarıldıktan sonra emir modülü artık requests gerektirmez. İkinci satır günlük kaydıdır; aynı içerik eşzamanlı olarak execution.log dosyasına ve standart çıktıya yazılır. Zaman damgası ile emir kimliği her çalıştırmada farklıdır; kalan çıktı karakterine kadar tekrarlanabilir:

yapılandırma: practice https://api-fxpractice.oanda.com token_length= 28
2026-11-02T12:00:00.011Z INFO execution TEST MODU [practice] emir hazır, gönderilmedi: {'order': {'type': 'MARKET', 'instrument': 'EUR_USD', 'units': '100', 'clientExtensions': {'id': '4ae82fd8-2de4-4117-97fc-c4aac3d11d99'}}}
1. test modu: gönderilmedi - OK
2. kapı [izinli olmayan araç]: İzinli olmayan araç: GBP_JPY
2. kapı [limit üstü büyüklük]: Büyüklük 5000 şu limiti aşıyor: 1000
2. kapı [sıfır büyüklük]: Büyüklük sıfırdan farklı tamsayı olmalı
2. kapı [sayı yerine mantıksal değer]: Büyüklük sıfırdan farklı tamsayı olmalı
3. kapı [onaysız canlı]: Canlı ana makine açık operatör onayı ister
tüm kapılar sınandı

Çıktı neyi doğrular, neyi doğrulamaz? Beş olumsuz durum beklenen engelleri devreye soktu: izin listesinde olmayan enstrüman, aşırı büyüklük, sıfır, mantıksal değer ve onaysız canlı mod. Assertion'lar test modunu da kontrol eder. Denetimde ayrıca ±1000 sınırları, limit aşımları ve geçersiz türler incelendi. İlk çağrı argümanı None, ayrıca emir gönderme işlevi uygulanmadığından bu test emir veremez. Çıktı; bir broker API'sini, günlük tutma mekanizmasının arızalara dayanıklılığını veya olası her hassas alanın maskelenmesini sınamaz. Sahte token yazdırılmaz: program yalnızca uzunluğunu gösterir. Kendi projenizde günlük kayıtlarının yazılmasını ve kurtarılmasını, hassas veri gizleme kurallarını ve disk arızalarını ayrıca kontrol edin.

Ancak bu sonuç, gerçek bir API karşısındaki davranış hakkında hiçbir şeyi doğrulamaz; doğrulayamaz da, çünkü bu modülde emir gönderen kod dalı hiç yoktur. Onun yerini yapısal bir taslak almıştır; böylece bu kodun herhangi bir şey gönderebileceği tek nokta da ortadan kalkmıştır. “Çalıştırmadık” ile “çalıştırılacak bir şey yok” arasındaki fark tam olarak budur: ilki okuyucuya doğrudan yapıştırılabilecek hazır bir parça bırakır; ikincisi ise kendisinin uygulaması ve bu sırada kendi brokerının belgelerini okuması gereken bir yapı bırakır.

Test, eksik bir güvenlik kontrolünü de tespit etmelidir. Bu nedenle test modu sonucunu doğrular ve beklenen istisnanın oluşmadığı her olumsuz test durumunun ardından AssertionError hatası üretir. Son mesaj, yalnızca bu kontroller geçildikten sonra görünür. Bu yine de küçük bir örnek testtir: broker yanıtlarının biçimlerini, her veri türünü veya durumun geri yüklenmesini kapsamaz. Daha büyük bir projede bu test durumlarını pytest içine taşıyın, olumlu test durumları ekleyin ve doğrulayıcı her değiştiğinde bunları çalıştırın.

Onay kontrolü, bir hatayı önleyen engeldir; bir programı durdurabilecek bir koruma değildir. Kapsamını açıkça belirtmek gerekir, çünkü bu kontrole kolayca olduğundan fazla anlam yüklenebilir. Onay değeri bu dosyanın içinde tanımlanan bir sabit değildir: program bu değeri BROKER_LIVE_CONFIRMATION ortam değişkeninden okur ve hesap numarasıyla karşılaştırır. Böylece operatör, işlem yapmak istediği hesabın numarasını kabuğa bilinçli olarak yazmak zorunda kalır. Bu, gerçekten yaşanabilecek üç hataya karşı yeterlidir: canlı ortam yapılandırmasını “sadece denemek için” çalıştırmak, hesapları karıştırmak ve test modunu yanlışlıkla kapatmak. Bunun ötesinde bir koruma sağlamaz. Aynı süreçte çalışan kod bu değişkeni kendisi ayarlayabilir; bu durumda onay kontrolü onu bir an bile durdurmaz. Bir programın kendi ortamından okuduğu hiçbir onay, bir programa karşı güvence oluşturmaz. Yetkilerin asıl sınırı kodunuzun dışındadır: broker böyle bir ayrım sunuyorsa emir verme yetkisi bulunmayan bir token ve hesap tarafında belirlenen pozisyon büyüklüğü sınırları [ÖZELLİK BROKERA/HESABA BAĞLI].

Birimler lot değildir. Yorum, MAX_UNITS alanının yanında boşuna durmuyor. Farklı API'ler pozisyon büyüklüğünü farklı kurallarla ifade eder: baz döviz birimi, lot veya sözleşme sayısı olarak. Buradaki bir hata ne sözdizimi hatasına ne de bir istisnaya yol açar. Kod doğru çalışırken, amaçladığınızdan iki ya da üç büyüklük mertebesi fazla piyasa maruziyeti yaratır. Herhangi bir şey göndermeden önce kullanılan birimi belgelerden kontrol edin ve bunu deneme hesabında asgari büyüklükte tek bir işlemle doğrulayın. Bu doğrulayıcıyı kendi hesaplamalarınıza bağlamadan önce iki sınırlamasını bilmeniz gerekir. pandas veya NumPy ile hesaplanan büyüklüğün türü numpy.int64 olur; bu tür, yerleşik int türünün alt sınıfı değildir. Değer her bakımdan bir tamsayı olsa bile doğrulayıcı, tamsayılarla ilgili bir mesaj vererek onu reddeder. Güvenli tarafta kalır, ancak kafa karıştırır. Bu nedenle DataFrame'den okunan bir sayıyı önce int(...) ile dönüştürün, ardından build_order() işlevine iletin. İkinci sınırlama şudur: order_id burada hiç kontrol edilmez ve bir tür açıklaması tek başına hiçbir kuralı zorunlu kılmaz. Mükerrer emirlerin tanınması bu tanımlayıcıya dayandığından, API belgelerinizde izin verilen uzunluğu ve biçimi kontrol edin; bunları da büyüklük sınırı gibi zorunlu olarak doğrulayın.

Günlükler, durum ve testler

Şunun yerine günlük: print(). Python’ın logging modülü; önem dereceleri, zaman damgası ve dosyaya yazma olanağı sunar. Ancak bunların hiçbiri kendiliğinden etkinleşmez. Yapılandırma yapılmadığında varsayılan seviye WARNING olur ve herhangi bir kayıt hedefi de yoktur. Dolayısıyla log.info(...) çağrıları sessizce atılır: kod günlük tutuyormuş gibi görünür, ancak hiçbir şey kaydetmez. Bir arızadan sonra günlüğün boş çıkmasının sık rastlanan nedenlerinden biri budur. Yapılandırma bir kez ve yalnızca programın giriş noktasında yapılır: içe aktardığınız modülde değil, çalıştırdığınız betikte. Yukarıdaki emir yürütme modülünde yer alan logging.basicConfig(...) çağrısı, yalnızca o örnekte modül aynı zamanda programın tamamını oluşturduğu için oradadır. Aynı koddaki yorum da daha büyük bir projede buranın yanlış yer olduğunu açıkça belirtir. Bunun somut bir nedeni vardır: modülün içinde bırakılan yapılandırma, içe aktarma sırasında çalışarak bütün sürecin günlük yapılandırmasını devralır. Ardından gerçek giriş noktasında yapılan logging.basicConfig(...) çağrısı hiçbir şey yapmaz: kök günlük kaydedicisinin hedefleri zaten tanımlanmışsa işlev etkisiz kalır ( force=True ile çağrılması dışında) ve bu durumu hiçbir hata mesajıyla da bildirmez [4]. Ayarlarınız basitçe kaybolur; kayıtlar, içe aktarılan modülün belirlediği yere gider. Bu, bir önceki cümlede anlatılan aynı sessiz kusurun başka bir yoldan ortaya çıkmasıdır. Kararı; dayanakları, isteğin içeriği ve sunucunun yanıtıyla birlikte kaydedin. Bir HTML hata sayfasının günlüğü doldurmasını önlemek için yanıtı kısaltın; sorunu teşhis ederken bu sınırı yükseltin. Tek istisna tokendır: onu asla kaydetmeyin. Brokerın yanıtını içermeyen bir günlük, sonradan sorunun mantıkta mı, ağda mı yoksa sunucu tarafında mı olduğunu belirlemenizi sağlamaz. Zaman damgaları logging.Formatter.converter = time.gmtime ve açık Z işaretiyle UTC kullanır. Bu ayar olmadan formatter normalde sistemin yerel saatini kullanır. Broker ve sistem günlüklerini aynı UTC temeliyle karşılaştırın; başka saat dilimini yalnızca görüntüleme için kullanın.

Emir yürütme günlüğü de hassas veridir. Tam emir içeriği execution.log dosyasına yazılır; eksiksiz bir programda brokerın yanıtı da kaydedilir: hesap numarası, emir tanımlayıcısı, pozisyon büyüklüğü ve fiyatlar. Token bu kayıtlarda bulunmaz ve bulunmamalıdır; testin on kez çalıştırılması on bir kayıt üretmiş, token ise hiçbir kayıtta yer almamıştır. Ancak kalan bilgiler bile bu dosyaya, içinde bir sır bulunan yapılandırma dosyasıyla aynı şekilde davranmayı gerektirir: execution.log dosyasını .gitignore içine, .env yanına koyun. Ayrıca .gitignore tek başına bir erişim kontrolü değildir: dosya izinlerini kısıtlamaz, belirli bir süre sonunda dosyayı silmez ve içeriğindeki hassas verileri gizlemez. Bu nedenle emir gönderme taslağı, hesap numarasının son dört karakteri dışındaki kısmını maskelemenizi ve günlüğe, sunucudan dönen her şeyi değil, kendi oluşturduğunuz listedeki alanları yazmanızı söyler. Böyle bir taslak gözetimsiz çalıştırılmadan önce iki konu daha düzeltilmelidir. Dosya yolu: execution.log göreli bir yol olarak belirtilmiştir; dolayısıyla dosya betiğin yanında değil, sürecin çalışma dizininde oluşturulur. Betik Task Scheduler veya cron ile başlatıldığında — aşağıda açıklandığı gibi, bu tür betikler tam da bu şekilde başlatılır — çalışma dizini betiğin dizininden oldukça farklı olabilir. Bu durumda arıza sonrasında toparlanma işleminin tamamının dayandığı günlük, onu aradığınız yerde bulunmaz. Betiğin konumundan veya yapılandırmadan hesaplanan mutlak bir yol verin. Dosya büyüklüğü: önceki paragraftaki öneri uygulandığında dosya sınırsız büyür, çünkü bu örnekte günlük döndürme mekanizması yoktur. logging.handlers.RotatingFileHandler aynı standart kitaplıkta bulunur ve import logging.handlers eklendikten sonra — çünkü import logging tek başına bu alt modülü kullanıma açmaz — FileHandler yerine, yapılandırmanın geri kalanını değiştirmeden kullanılabilir.

Durum, yeniden başlatma sonrasında da korunmalıdır. Süreç bir noktada durdurulacaktır: bir sistem güncellemesi, elektrik kesintisi veya sizin bastığınız Ctrl+C nedeniyle. Gönderilen emirlerle ilgili bilgiler yalnızca sürecin belleğinde tutuluyorsa, program yeniden başlatıldıktan sonra daha önce ne yaptığını bilemez. Asgari çözüm, gönderilen emirlerin tanımlayıcılarını göndermeden önce bir dosyaya veya veritabanına yazmak ve program açıldığında bu kaydı API'den alınan hesap durumuyla karşılaştırmaktır. Bağlantı koptuktan sonra aynı pozisyona iki kez girilmesini önleyen işlem tam olarak budur.

Ağ bağlantısı olmadan test. Sinyal mantığı ve sınırların doğrulanması, hiçbir yere bağlanmadan hazırlanmış veriler üzerinde test edilebilir; yukarıda güvenlik kontrolleriyle yapılan da tam olarak budur. Çalışan bir API gerektiren test, birim testi değildir ve her kod değişikliğinde çalıştırılamaz. Çevrimdışı kontrol edilebilen unsurların testleriyle başlayın: giriş koşulu, pozisyon büyüklüğünün hesaplanması, boş veri ve aralıklarında eksik kayıt bulunan veri karşısındaki davranış.

Kontrollü durdurma. Emir yürüten bir program, sonlandırma sinyaline tepki vermelidir: bir emri gönderirken yarıda kesilmek yerine, sürmekte olan işlemi tamamlamalı, durumu kaydetmeli ve kapanmalıdır. Bunun yanında, günlük zarar eşiği aşıldığında yeni girişleri durduran ayrı bir koşul da bulunmalıdır. Bu eşiğin nereye konulacağı ve nasıl hesaplanacağı kursun risk yönetimi bölümünde açıklanır; kodda ise her girişten önce kontrol edilen sıradan bir koşuldur, bir kitaplık işlevi değildir.

Nerede çalıştırılır? Kesintisiz çalışan bir sunucu, sistem saatinin eşitlenmesi, yeniden başlatma sonrasında çalışmanın devam etmesi ve arıza sonrası toparlanma prosedürü şu makalede ele alınır: İşlem otomasyonu: EA'lar, betikler ve VPS. Bir betiği başlatmanın birkaç yolu vardır; aralarındaki fark kullanım kolaylığı değil, arızadan sonra ne olduğudur. Windows'ta Task Scheduler veya Unix sistemlerinde cron betiği belirli aralıklarla başlatır ve çalışmasını tamamladıktan sonra sonlandırır. Bir sistem hizmeti — örneğin bir systemd birimi — süreci sürekli çalışır durumda tutar ve süreç kapanırsa yeniden başlatabilir. Süreç yöneticisi de aynı işi uygulama katmanında yapar. Kısa aralıklarla çalışıp kapanan bir betik ile uzun süre çalışan bir süreç arasındaki seçim, durumu nerede saklayacağınızı ve aynı anda yalnızca tek örneğin çalışmasını kimin sağlayacağını belirler. Python kodu açısından önemli olan şudur: zamanlayıcıdan başlatılan betik, önceki çalışmanın henüz bitmemiş olabileceğini hesaba katmalıdır. Yani sıfırdan başladığını varsaymak yerine, işlem yapmadan önce durumu kontrol etmelidir.

Ayrıca bkz. Algoritmik işlem — sistem mimarisi ve karar sürecinin tamamında otomasyonun yeri.

9. Python'da geleceği görme ve veri sızıntısı

Gelecekteki bilgiyi kullanma, bir yöntem hatası değildir. Tek bir kod satırındaki hatadır; bu nedenle de test yöntemlerini anlatan yazıdan çok bu makalenin konusudur. Belirli bir ana atanan sonucun, o anda henüz mevcut olmayan bilgilerle hesaplanması anlamına gelir.

pandas'taki en yaygın biçimi son derece basittir: sinyal sütununu aynı satırdaki getiri sütunuyla çarpmak. t satırındaki getiri, önceki mumun kapanışı ile t mumunun kapanışı arasında oluşmuştur. t mumunun kapanışından hesaplanan sinyal ise ancak bu getiri zaten gerçekleştikten sonra bilinir. İkisini çarptığınızda, karar anında hakkında hiçbir şey bilmediğiniz bir hareketin getirisini kendinize yazmış olursunuz.

Aşağıdaki örneğin varsayımları: sekiz sentetik saatlik gözlem, UTC zaman diliminde bir indeks, temsili değerler ve işlem maliyetlerinin hesaba katılmaması. Amaç bir kuralı değerlendirmek değil, kaydırma mekanizmasını göstermektir. Ortam, bölüm 2 ile aynıdır: Python 3.12.10, pandas 3.0.5.

"""Tek satırda geleceği görme: aynı kural, iki sonuç."""
import pandas as pd

index = pd.date_range("2026-06-01", periods=8, freq="1h", tz="UTC")
df = pd.DataFrame(
    {"close": [1.0800, 1.0810, 1.0825, 1.0815, 1.0830, 1.0845, 1.0840, 1.0855]},
    index=index,
)
df["sma"] = df["close"].rolling(3).mean()
df["return"] = df["close"].pct_change()

# Kural: kapanış üç mum ortalamasından yüksekse uzun.
df["position"] = (df["close"] > df["sma"]).astype(int)

# YANLIŞ: t satırı pozisyonu aynı satırın getirisiyle çarpılır.
# t getirisi t-1 kapanışı ile t kapanışı arasında oluştu; karar
# girişte henüz olmayan fiyatı kullanır.
df["result_with_lookahead"] = df["position"] * df["return"]

# DOĞRU: t kapanışında seçilen pozisyon yalnızca t+1 getirisinde çalışır.
df["result_without_lookahead"] = df["position"].shift(1) * df["return"]

print(df[["result_with_lookahead", "result_without_lookahead"]].sum().round(6))

Çalıştırma çıktısı:

result_with_lookahead       0.005082
result_without_lookahead    0.001384
dtype: float64

Yorum. Aynı kural, aynı veri, tek shift(1) çağrısı kadar bir fark; buna rağmen basit dönemsel getirilerin toplamı 0.005082'den 0.001384'e, yani başlangıç değerinin yüzde 30'undan daha azına düşer. Sekiz gözlemde bu elbette bir ölçüm değil, yalnızca gösterimdir. Aynı hata, birkaç yıllık veri üzerinde sıradan bir stratejiyi olağanüstü, olağanüstü bir stratejiyi ise gerçekleşmesi imkânsız gösterebilir. Her iki değer de boyutsuzdur ve spread ile komisyonu içermez. Bu maliyetler düşüldüğünde doğru sürümün sonucu daha da düşük olurdu.

Hesaplamanın kendisiyle ilgili bir çekince daha vardır. pct_change() basit getirileri verir. Bölüm 4'te belirtildiği gibi bu getiriler toplanarak değil, çarpılarak bileşikleştirilir; yalnızca logaritmik getiriler toplanabilir. Burada toplama yapılması bilinçli bir yaklaşımdır: binde mertebesindeki değerlerde, toplam ile doğru bileşik hesaplama arasındaki fark bu örnekte 0.00001'den küçüktür. Bu fark, göstermek istediğimiz farkın iki büyüklük mertebesinden de fazla altındadır. Kaydırma mekanizmasını değil, bir hesabın sonucunu ifade etmesi gereken hesaplamada (1 + r) değerleri çarpılarak bileşikleştirilir veya logaritmik getirilere geçilip ancak ondan sonra toplama yapılır.

Çifte kaydırmaya dikkat. Kaydırma tek bir yerde uygulanır: ya pozisyon sütununa ya da sinyalin girdi verilerine; hiçbir zaman ikisine birden değil. Önce kaydırılmış fiyatlardan sinyal hesaplayıp ardından pozisyonu da kaydıran kod, girişi iki dönem geciktirir ve sonucu sessizce olduğundan düşük gösterir. Bu yöndeki hata, gelecekteki bilgiyi kullanmaktan daha az tehlikelidir; ancak aynı ölçüde yanlıştır.

Yanlı ve kaymış getiri eşleme fiyat tablosu
Burada gelecek kapanışı −%0.98'i görünürde +%2.00 yapar.

Python kodunda gelecekteki bilgiyi kullanmanın diğer biçimleri — hepsi aynı soruya indirgenir: bu değer, karar verildiği anda biliniyor muydu?

  • Mevcut mumun en yüksek veya en düşük fiyatını, ancak mum kapandıktan sonra öğrenebileceğiniz hâlde giriş koşulunda kullanmak
  • Veriyi eğitim ve doğrulama bölümlerine ayırmadan önce ölçeklendirme veya normalizasyonu bütün veri kümesi üzerinde hesaplamak; bu durumda ölçeklendirme parametreleri gelecekten bilgi taşır
  • Sonraki mumları kullanarak yumuşatma yapan veya dönüş noktalarını işaretleyen göstergeler kullanmak; bunların tarihsel değeri, canlı olarak gördüğünüz değerden farklıdır
  • Makroekonomik verilerin ilk yayımlanan değerleri yerine revizyon sonrasındaki sürümlerini kullanmak
  • Emrin, sinyalin oluştuğu mumun kapanış fiyatından gerçekleştiğini varsaymak

Veri hazırlamada sızıntı. Listedeki ikinci madde ayrı bir açıklamayı hak eder, çünkü Python'da bu hata düşünmeden yapılabilir. Dönemlere ayırmadan önce ölçekleyiciyi bütün DataFrame üzerinde çağırmak tek satırdır ve zararsız görünür; ancak gelecekteki veri dağılımına ilişkin bilgiyi doğrulama bölümüne taşır. Bir dönüşümün parametreleri yalnızca önceki veriler üzerinde belirlenir ve sonraki verilere uygulanır; hiçbir zaman tersi yapılmaz.

Sinyali güncel veya sonraki getiriye atayan iki kod
Kaydırma modelin belirli zaman hatasını düzeltir; kârlılık kanıtı değildir.

Çoklu sınama. Aynı veri üzerinde ne kadar çok varyant denerseniz, bunların en iyisinin tesadüfen iyi çıkma olasılığı o kadar artar. Açıkça tanımlanmış bir sıfır hipotezi modeli altında, 10000 testin her birinin yanlış pozitif olasılığı 0.05 ise “anlamlı” olarak etiketlenen sonuçların beklenen sayısı 0.05 × 10000 = 500 olur. Bu beklenen değer, olasılıkların toplanmasından çıkar ve testlerin bağımsız olmasını gerektirmez. Bu varsayımlar altında bağımlılık, sonuç sayısının ortalamasını değil dağılımını değiştirir. Sayının büyüklük mertebesi, sonucun yanında kaç deneme yapıldığının da neden bildirilmesi gerektiğini gösterir.

Deneme sayısını hesaba katan biçimsel bir düzeltme vardır: Bailey ve López de Prado'nun 2014'te önerdiği Deflated Sharpe Ratio [12]. Hazır bir uygulamaya başvurmadan önce, onu bulduğunuz kitaplığın durumunu ve lisansını kontrol edin; bu alandaki bazı projeler dağıtım modellerini değiştirmiştir. Formülün kendisi kaynak yayında açıklanır ve bağımsız olarak uygulanabilir.

Bölümün kapsamadıkları. Aşırı uyum, örneklem seçimi, ilerleyen pencereli analiz, hayatta kalma yanlılığı ve artan maliyetler karşısında sonucun dayanıklılığını sınamak, test yöntemlerinin konusudur ve geriye dönük test ve ileri test makalesinde açıklanır. Burada yalnızca kodda neyin yanlış yapılabileceği ve kod içinde neyin kontrol edilebileceğiyle ilgileniyoruz.

Sonuca güvenmeden önce kodda kontrol edilecekler:

  1. Veri kümesinde t satırı için hesaplanan sonuç, yalnızca karar anından önce mevcut olan verileri mi kullanıyor?
  2. Kaydırma tam olarak bir kez, sinyale veya veriye mi uygulandı; ikisine birden uygulanmadı mı?
  3. Emrin gerçekleşme zamanı ve fiyatı, örtük bırakılmak yerine açıkça belirtilmiş mi?
  4. İşlem maliyetleri modele dâhil mi; yoksa bilinçli olarak dışarıda bırakılıp bu durum açıkça belirtilmiş mi?
  5. Veriler eğitim ve doğrulama bölümlerine kronolojik olarak mı ayrılmış?
  6. Veri dönüşümleri yalnızca daha önceki bölüm üzerinde mi belirlenmiş?
  7. Test edilen varyantların sayısı sonuçla birlikte kaydedilmiş mi?
  8. Zaman indeksinin saat dilimi açıkça tanımlanmış mı; indeks, yinelenen kayıtlar olmadan artan sırada mı ilerliyor?
Pratik kural: şaşırtıcı derecede iyi bir sonuç, bir avantajdan önce bir hata olabileceği varsayımını doğurmalıdır. Olağanüstü bir şey bulduğunuza karar vermeden önce yukarıdaki listeyi madde madde inceleyin ve kodu, sonucun ne olması gerektiğini bildiğiniz veriler üzerinde çalıştırın. Örneğin sabit fiyatlı bir seride, maliyetlerden önce fiyat değişiminden doğan getiri sıfırdır; maliyetler düşüldüğünde işlemler zarar edebilir. Yanıtı bilinen verilerle yapılan bir test, özkaynak eğrisine bakıp durmaktan daha fazla hata ortaya çıkarır.

10. Gerçekte ne kadar kod gerekir?

Yazılım geliştirici olmak zorunda değilsiniz. Kendi kodunuzun veya bir depodan indirdiğiniz başkasına ait bir betiğin sizi yanıltmasına izin vermeyecek kadar bilgi sahibi olmalısınız.

DüzeyBecerilerNe sağlar?Yaklaşık süre
0: kod yokYokPlatformun izin verdiği şeyleri, onun dayattığı biçimde analiz edersiniz—
1: kod okumaBaşkasının betiğini anlar, parametrelerini değiştirir ve yalıtılmış bir ortamda çalıştırırsınızDepodan alınan bir betiğin gelecekteki bilgiyi kullanıp kullanmadığını ve maliyetleri hiç hesaba katıp katmadığını kontrol edebilirsinizBirkaç hafta
2: kendi analizinizpandas, numpy, matplotlib; kendi analiz betiklerinizHesap tablosunun sınırlarına takılmadan kendi işlem günlüğünüzü ve piyasa verilerini analiz etmeBirkaç ay
3: entegrasyonAPI'ler, hata yönetimi, günlük tutma, zamanlama ve testlerBildirimler ve onay gerektiren emirlerin hazırlanmasıAltı ay ve üzeri
4: emir yürütme koduOtomatik testler, durum yönetimi, devreye alma ve izlemeHer karar için ayrı onay alınmadan çalışmasına izin verilen bir programYıllarla ölçülen uygulama deneyimi

Son sütundaki süreler yaklaşık değerlerdir; nereden başladığınıza ve haftada gerçekten kaç saat ayırdığınıza bağlıdır. Bunları bir vaat olarak değil, gereken zamanın büyüklük mertebesi olarak değerlendirin.

Düzey 1 ve 2 piyasada avantaj sağlamaz. Sağladıkları şey, başkasının çözümünün anlamlı olup olmadığını denetleyebilmek ve doğrulanamayan sistemleri satın almaktan vazgeçebilmektir. Bu gerçek bir değerdir; ancak hazır araç satıcılarının pazarladığı değerden farklıdır. Düzey 4, daha iyi işlem yapmak için değil, emir yürüten bir programın bakımını üstlenmek istiyorsanız gereklidir. Python'ın finanstaki kullanım alanlarına genel bir bakış Hilpisch'in kitabında sunulur [11], ancak kodla ilgili her kitap için geçerli bir çekinceyle: yayımlanmasından bu yana bazı arayüzler değiştiği için, kod parçaları kullanılmadan önce kitaplıkların güncel sürümleriyle karşılaştırılarak kontrol edilmelidir.

Dil modelinin ürettiği kod. Asistanlar, analiz kodunun büyük bir bölümünü sizden daha hızlı yazabilir. Aynı zamanda, doğru görünen ama gelecekteki bilgiyi kullanan kod üretebilir ve kurulu kitaplık sürümünüzde bulunmayan bir işleve atıfta bulunabilirler. Düzey 1 becerileri olmadan bunların hiçbirini fark edemezsiniz. Dil modellerinin neler yapabildiği ve neler yapamadığı şu ayrı makalede açıklanır: İşlemlerde yapay zekâ ve LLM'ler.

Mantıklı öğrenme sırası — her adım bir öncekine dayanır ve çalışan bir sonuçla tamamlanır:

  1. Dil temelleri — değişkenler, listeler, işlevler ve koşullu ifadeler. Başkasının betiğini tahmin yürütmeden okuyabilecek kadar bilgi
  2. Kendi verinizde pandas — işlem günlüğünüzden dışa aktardığınız bir dosyayı yükleyin; haftanın gününe ve finansal araca göre ayrılmış istatistikler hesaplayın. Belgeler yerine bir kitaptan öğrenmeyi tercih ediyorsanız, bu aşama pandas'ın yazarı McKinney'nin kitabında bütünüyle ele alınır [10]
  3. Grafik — önceki adımda hesapladıklarınızdan bir özkaynak eğrisi ve bir sermaye düşüşü grafiği çizin
  4. Veri kalite kontrolleri — bölüm 4'teki örneği kendi dosyanız üzerinde tekrarlayın: saat dilimi, yinelenen kayıtlar ve veri boşlukları
  5. Salt okunur entegrasyon — sağlayıcı bu yetki ayrımını sunuyorsa, işlem yapma yetkisi olmayan bir token kullanarak deneme hesabındaki bir API'den veri alın

Bu sıralama bilinçlidir. Beşinci adıma ancak indirdiğiniz verileri kontrol edebildiğinizde geçilir; aksi takdirde işi değil, bir hatayı otomatikleştirirsiniz.

En değerli kullanım en az gösterişli olandır. Kendi işlem günlüğünüzü pandas ile analiz etmek, sonucun gerçekte nereden kaynaklandığını ve nerede yalnızca öyle zannettiğinizi gösterir. Bu genellikle bir emir yürütme programı kurmaktan daha fazla yarar sağlar ve yalnızca düzey 2 gerektirir. Bunun nedeni birinin böyle bir ölçüm yapmış olması değil, başkasının verileri yerine sizin kendi verileriniz üzerinde çalışan tek analizin bu olmasıdır.

Tek bir görevle başlayın: kendi verilerinizi yükleyin, üç satır üzerinde elle kontrol edebileceğiniz bir şeyi hesaplayın ve sonuçları karşılaştırın. Python'ın sonucu parmak hesabınızla uyuşuyorsa bir temeliniz var demektir. Uyuşmuyorsa ilk hatanızı buldunuz; bu da ilk seferde çalışan ama anlamadığınız bir betikten daha iyi bir başlangıçtır.

Python kötü bir stratejiyi düzeltmez. En iyi ihtimalle, onun kötü olduğunu size daha hızlı gösterir.

SSS: işlemlerde Python

Python işlem otomasyonu için yeterince hızlı mı?
Darboğazın nerede olduğuna bağlıdır. Bir sonraki H1 mumunun açılışında giren stratejide Python hesap süresi, emrin ağ ve broker sunucusundan geçişine kıyasla önemsizdir. Bir dakikalık mumun içinde işlem yaparken zincirin tamamı önem kazanır: karar anındaki verinin yaşı, uç noktanın yanıt süresi ve gerçekleşme onayına kadar geçen süre. Bu değerlerin hiçbiri ezberden aktarılamaz; kendi brokerınızda kendi zincirinizde ölçülmeli, tarih ve deneme sayısıyla kaydedilmelidir. Python çok düşük gecikmeli işlem için uygun değildir; ancak bu, bireysel işlemden farklı bir sorun sınıfıdır.
Python'u işlemlerde yararlı olacak düzeyde öğrenmem ne kadar sürer?
Başkasının kodunu okuyup parametrelerini değiştirebilmek için birkaç hafta düzenli çalışma; pandas ile kendi günlüğünüzü ve grafiklerinizi bağımsız analiz etmek için birkaç ay gerekir. Bu aralıklar başlangıç noktanıza ve haftalık saatlerinize bağlıdır; büyüklük sırası olarak görün. Tüm dili kapsayan kurs gerekmez: dosya yükleyip istatistik hesaplamak için sınıflar, dekoratörler veya eşzamanlılık şart değildir. En hızlı kendi verinizde öğrenirsiniz; çünkü sonucun anlamlı olup olmadığını hemen bilirsiniz.
Python'u MT4 veya MT5 hesabına bağlayabilir miyim?
MT5 için resmî MetaTrader5 pip üzerinden kurulan paket vardır; ancak yalnızca Windows'ta. macOS ve Linux için resmî kurulum yoktur; terminali Python ile birlikte Wine altında çalıştırmanız veya ayrı aracı katman kurmanız gerekir [ÖZELLİK SÜRÜME/ORTAMA BAĞLI]. Paketin ne yaptığını da anlamalısınız: broker sunucusuyla doğrudan değil, aynı makinede kurulu MetaTrader 5 terminaliyle iletişir. Terminal kurulmuş olmalıdır, önceden başlatılmış olması gerekmez. Sağlayıcının belgesine göre initialize() çağrısı gerektiğinde terminali kendisi başlatır. Giriş parametreleri isteğe bağlıdır; yokluklarında terminalde saklanan veriler kullanılır [18]. Bu kozmetik ayrıntı değildir: zamanlayıcıdaki betik sizin katılımınız olmadan terminali açıp giriş yapabilir. Bu kullanışlıdır ama “terminal kapalıydı” bilgisinin tek başına koruma olmadığı anlamına gelir. Entegrasyon 2019 terminal sürümünde, build 2085 ile getirildi [7]. Brokerdan bağımsız REST API değildir, öyle tanımlanmamalıdır. Alternatif harici bulut köprüsüdür; bir halka daha ekler ve erişimi üçüncü tarafa emanet etmeyi gerektirir. Başlangıç için en basit ve güvenli yol tek yönlü kalır: veriyi terminalden dosyaya aktarın, Python'da analiz edin; karar ve gerçekleşme insanda olsun.
API anahtarlarını nerede saklamalıyım ve bir anahtar sızarsa ne yapmalıyım?
Yalnızca ortam değişkenlerinde veya depo dışında tutulan ve .gitignore içinde listelenen bir yapılandırma dosyasında. Asla kodda, yorumda veya notebook'ta değil: notebook hücresi çıktıyı da kaydeder, dolayısıyla yazdırılan token dosyada kalır. Anahtar depoya girdiyse satırı silmek hiçbir şey çözmez; değer sürüm geçmişinde kalır. Tek etkili yanıt, anahtarı broker tarafında iptal edip yenisini üretmek ve ancak bundan sonra geçmişi temizlemektir. Bundan bağımsız olarak, kodunuzdan ayrı ikinci koruma katmanı olarak hesap tarafında pozisyon büyüklüğü kısıtları koymak yararlıdır.
Bu makaledeki kod kontrol edildi mi?
Evet; metin her kod bloğunun durumunu ayrı belirtir. Bölüm 4'teki CSV yükleme örneği ve bölüm 9'daki sinyal kaydırma karşılaştırması belirtilen ortamda (Python 3.12.10, pandas 3.0.5) çalıştırıldı; gösterilenler programın gerçek çıktılarıdır. Yapılandırma yükleme bloğu eksik değişkenlerden tanınmayan ortam adının reddine kadar beş dalın tümünde sınandı. Emir katmanının güvenlik kapıları çevrimdışı, ağ ve sır olmadan kontrol edildi. API'den veri alan parça, bellekteki HTTP oturumu taklidiyle sınandı; sunucu açılmadı ve hiçbir ağ bağlantısı, şu adresle de dâhil, kurulmadı: 127.0.0.1 ve brokerla da bağlantı kurulmadı. Belgelenmiş yanıt işleme senaryoları, deneme limitinin tükenmesi ve bağlantı istisnası dahil, sınandı. Ancak makalede emir gönderecek tek bir satır yoktur: gönderim dalı yapısal taslakla değiştirildi. Çünkü işlem yapan kod başkasının hesabında test edilmez; test edilmemiş hazır bir fonksiyon da yalnızca doğrulanmamış koda güvenmeyi öğretirdi. Kodu çalıştırmak, başkasının sunucusuna ilişkin varsayımları doğrulamakla aynı şey değildir. Uç nokta yollarını, yanıt yapılarını ve limitleri kendi brokerınızın belgelerinde ve bir deneme hesabında kontrol edin.
Herkese açık bir depoda bulunan stratejiye güvenebilir miyim?
Kendiniz kontrol etmeden hayır; mesele yazarların kötü niyeti değildir. Tipik eksikler, denetlenmemiş sinyal kaydırması, maliyet modelinin olmaması, ortamın ve kitaplık sürümlerinin açıklanmaması ve gösterilen varyanttan önce kaç varyantın denendiği bilgisinin yokluğudur. Herhangi bir şeyi çalıştırmadan önce sinyalin nerede oluştuğunu ve sonucun hangi döneme atandığını okuyun. Maliyetlerin kodda hiç yer alıp almadığını kontrol edin, bağımlılık dosyasından ortamı yeniden oluşturun ve yalıtılmış biçimde çalıştırın. Depo kitaplık sürümlerini belirtmiyorsa sonuç zaten yeniden üretilebilir değildir; yeniden üretilemeyen sonuç ise ne doğrulanabilir ne de çürütülebilir.

Kaynaklar ve başvurular

  1. pandas 3.0.5 belgeleri: pandas.pydata.org/docs — veri yapıları, zaman dizini ve saat dilimleri, resample, eksik değerlerin işlenmesi; sürüm kontrol tarihi 2026-08-22
  2. pandas — 2.2.0 ve 3.0.0 sürüm notları — pandas.pydata.org/docs/whatsnew — method parametresinin şu çağrıda kullanımdan kaldırılacak olarak işaretlenmesi: fillna() ve varsayılan davranıştaki değişiklikler
  3. NumPy: belge ve sürüm notları — numpy.org/doc — vektörleştirme, rastgele sayı üreteçleri ve tohum belirleme
  4. Python belgeleri — docs.python.org/3 — şu modüller: venv, logging ve timeit ve istisna işleme
  5. OANDA v20: En İyi Uygulamalar — developer.oanda.com/rest-live-v20/best-practices — önerilen mevcut bağlantı üzerindeki istek sıklığı (100/s) ve yeni bağlantı sıklığı (2/s); kontrol tarihi 2026-08-22
  6. MQL5: Python entegrasyonu — mql5.com/en/docs/python_metatrader5 — MetaTrader5 paketi ve terminalle iletişimi
  7. MetaTrader 5: build 2085 notları — metatrader5.com/en/releasenotes/terminal/2085 — terminalin Python entegrasyonunun kullanıma sunulması
  8. cTrader Algo belgeleri — help.ctrader.com/ctrader-algo — cBot'lar, göstergeler ve eklentiler için yerleşik C# ve Python desteği; durum tarihi 2026-08-22
  9. TA-Lib (Python bağları) — pypi.org/project/TA-Lib — PyPI'da yayımlanan ikili paketler, C kitaplığını derlemeden kurulum
  10. McKinney, W. — Veri Analizi için Python, O'Reilly, 3. baskı, 2022 — pandas, numpy ve zaman serileriyle çalışma
  11. Hilpisch, Y. — Finans için Python, O'Reilly, 2. baskı, 2018 — Python'un finansta kullanımları; kod parçalarını güncel kitaplık sürümlerine göre kontrol edin
  12. Bailey, D. H., López de Prado, M. — Düzeltilmiş Sharpe Oranı, Journal of Portfolio Management, 2014 — Sharpe oranının denenen varyant sayısına göre düzeltilmesi
  13. PyPI — paket sayfaları ve kayıt sisteminin JSON arayüzü — pypi.org — metinde anılan kitaplıkların en son sürümlerinin tarih ve numaraları (mplfinance, streamlit, openpyxl, pandas-ta, backtrader, vectorbt, backtesting.py, ib_insync, ib_async); kontrol 2026-10-08
  14. OANDA v20: Order ve Transaction API — developer.oanda.com/rest-live-v20/transaction-df – clientExtensions emre eklenen isteğe bağlı özel veriler olarak ve MT4 ile ilişkili hesaplarda bunu ayarlamama uyarısı; piyasa emirlerinin idempotent olduğuna dair belgelenmiş garanti yoktur; kontrol tarihi 2026-08-22
  15. OANDA v20: Geliştirme Rehberi — developer.oanda.com/rest-live-v20/development-guide — zorunlu olarak uygulanan REST limiti saniyede 120 istek ve HTTP 429 yanıtı; istek gönderen IP adresi başına 20 etkin akış ve saniyede 2 yeni bağlantı sınırı; kontrol tarihi 2026-08-22
  16. Microsoft: Excel özellik ve sınırları — support.microsoft.com — çalışma sayfası boyutu: 1048576 satır ve 16384 sütun; kontrol tarihi 2026-08-22
  17. İsviçre Merkez Bankası: 15 Ocak 2015 duyurusu — snb.ch — euro başına 1.20 franklık asgari kurun kaldırılması ve faizin yüzde −0.75'e indirilmesi
  18. MQL5 — MetaTrader5.initialize() – mql5.com/en/docs/python_metatrader5/mt5initialize_py — bağlantı kurulurken gerekirse terminalin başlatılması ve isteğe bağlı path, login, password ve server parametreleri ile terminalde saklanan veriler; kontrol tarihi 2026-08-22

Öğrenme ilerlemeniz

Bu dersi bitirdiniz mi? Kendiniz işaretleyin. Sayfayı açmak veya kaydırmak dersi tamamlanmış saymaz.

İlerleme için JavaScript ve tarayıcı depolaması gerekir.

Yalnızca bu tarayıcıda saklanır. Hesap veya cihazlar arası eşitleme yoktur. Site verilerini silmek ilerlemenizi silebilir.

Jarosław Wasiński

Baş editör, ForexMechanics.com • Makro ve FX analisti

Finansal piyasalarda 20 yılı aşkın deneyime sahip bağımsız ve profesyonel analist. 2007'den bu yana Forex piyasasında aktif; temel analiz, OTC piyasa yapısı ve disiplinli sermaye risk yönetimi alanlarında uzmanlaşmıştır.

  • 2004'ten itibaren güvenilir finansal eğitim sunan MyBank.pl'in kurucusu ve baş editörüdür.
  • Yatırımcılar ve traderlar için yüzlerce derinlikli piyasa yorumu, yapısal analiz ve eğitim materyalinin yazarıdır.
  • Piyasa şeffaflığının savunucusudur; somut verilere ve kurumsal raporlara (BIS, ESMA, CFTC) dayalı eğitimi destekler.

Tüm içerikler yalnızca eğitim ve bilgilendirme amaçlıdır – yatırım tavsiyesi veya herhangi bir öneri niteliği taşımaz. Unutmayın: kaldıraçlı işlemler (Forex/CFD) hızlı sermaye kaybı riski taşır.