Giriş ve çıkış
Bir gösterge grafiğe çizgi koyar, strateji ise emir verir. Bu sayfada pozisyona nasıl girildiğini, nasıl çıkıldığını ve sonucun sınama özetinde nasıl okunduğunu öğreneceksin.
Strateji yazmak iki soruya cevap vermektir: ne zaman girilir, ne zaman çıkılır. Geri kalan her şey bu iki cümlenin süsüdür. Önce en sade hâlini kur, koşulları sonra ekle.
Emir ne zaman dolar
Bir emir, kendi barı kapanmadan dolamaz. Koşulun doğru olduğu bara sinyal barı denir. Piyasa emri bir sonraki barın açılış fiyatından gerçekleşir. Bu yüzden grafikteki giriş işareti sinyal barında değil, ondan sonraki barda görünür.
- Koşullar bar kapanışında ölçülür.
- Piyasa emri sonraki barın açılışında dolar; giriş de çıkış da böyle.
- Bekleyen emir farklıdır: fiyat seviyeye değdiği anda, bar içinde dolar.
Bu bir gecikme değil, dürüstlük kuralıdır. Emri sinyal barının kapanışından doldurmak, henüz oluşmamış bir fiyatı bildiğini varsaymak olurdu. Sınama sonucu da olduğundan güzel çıkardı.
Uzun pozisyona girmek
enterLong bir koşul alır ve koşulun doğru olduğu barda uzun pozisyon açar. Koşul her barda yeniden ölçülür. Bu yüzden uzun süre doğru kalan bir kural yerine, yalnız dönüş anında doğru olan bir kural yazmak istersin. crossover tam bunu yapar: iki seri kesiştiği tek barda doğru olur.
// hizli ortalama yavas ortalamayi yukari kesince gir hizli = sma(close, 10) yavas = sma(close, 30) enterLong(crossover(hizli, yavas)) exit(crossunder(hizli, yavas)) plot(hizli, "hizli", "#26a69a") plot(yavas, "yavas", "#ef5350")
Fiyat panosunda yeşil 10 barlık ve kırmızı 30 barlık ortalama çizilir. Yeşil kırmızıyı yukarı kestikten sonraki barda bir alış işareti, aşağı kestikten sonraki barda bir çıkış işareti belirir.
Pozisyon açıkken aynı yönde ikinci bir enterLong koşulu doğarsa hiçbir şey olmaz. Varsayılan olarak aynı yönde tek giriş taşınır. Kademeli eklemek istiyorsan tavanı pyramiding ile yükseltmen gerekir.
enterLong işlevi
enterLong(condition, stopLoss?, takeProfit?, id?, limit?, stop?)
Değer döndürmez; sınamaya bir uzun giriş emri bildirir.
Kısa pozisyona girmek
enterShort aynı işi ters yönde yapar. Fiyat düştüğünde kazanan bir pozisyon açar. Çıkış tarafı yine exit ile yönetilir; ayrı bir kapatma işlevi öğrenmen gerekmez.
// asagi kesiste kisa pozisyon, yukari kesiste kapan ort = sma(close, 20) enterShort(crossunder(close, ort)) exit(crossover(close, ort)) plot(ort, "ortalama", "#2962ff")
Kapanış 20 barlık ortalamanın altına indikten sonraki barda bir satış işareti çıkar ve kısa pozisyon açılır. Kapanış ortalamanın üstüne döndüğünde pozisyon kapanır.
Spot modda açığa satış yoktur. enterShort orada emir üretmez; koşul doğru olsa bile pozisyon açılmaz.
enterShort işlevi
enterShort(condition, stopLoss?, takeProfit?, id?, limit?, stop?)
Değer döndürmez; sınamaya bir kısa giriş emri bildirir.
Girişle birlikte koruma kurmak
Çıkışı ayrı bir satıra yazmak zorunda değilsin. enterLong'a stopLoss ve takeProfit verirsen, pozisyon açıldığı anda iki koruma seviyesi de kurulur. İkisi de yüzdedir ve giriş fiyatına göre ölçülür.
// girisle birlikte zarar-kes ve kar-al kur enterLong(crossover(close, sma(close, 20)), stopLoss=2, takeProfit=4) plot(sma(close, 20), "ortalama", "#2962ff")
Her kesişimden sonraki barda pozisyon açılır. Pozisyon ya giriş fiyatının yüzde 2 altında zarar-kes ile ya da yüzde 4 üstünde kâr-al ile kapanır. Ayrı bir exit satırına gerek kalmaz.
Bu seviyeler bar içinde denetlenir. Aynı barda hem hedef hem zarar seviyesi görülürse fiyatın bar içinde hangisine önce uğradığına bakılır; tam eşitlikte zarar-kes önce çalışır.
Çıkmak: exit
exit'in ilk argümanı her zaman bir koşuldur. Boş bırakırsan betik hata verir, çünkü çıkışın ne zaman olacağı belirsiz kalır. Koşul doğru olduğu barda karar verilir, dolum bir sonraki barın açılışındadır.
Girişe id verirsen çıkışı o girişe yöneltebilirsin. qtyPct ile de tamamı yerine bir yüzdesini kapatırsın. Yüzde, o an kalan büyüklük üzerinden hesaplanır; başlangıç büyüklüğü üzerinden değil.
// girise ad ver, kari iki kademede al enterLong(crossover(close, sma(close, 20)), id='ana') exit(crossover(rsi(close, 14), 60), fromEntry='ana', qtyPct=50) exit(crossover(rsi(close, 14), 70), fromEntry='ana') plot(sma(close, 20), "ortalama", "#2962ff")
Kesişimden sonra pozisyon açılır. RSI 60'ı yukarı kestiğinde pozisyonun yarısı kapanır ve büyüklük 1'den 0.5'e iner; RSI 70'i kestiğinde kalan da kapanır. Grafikte iki ayrı çıkış işareti farklı fiyat seviyelerinde görünür.
- fromEntry yalnız o adlı girişi hedefler. Ad vermezsen tüm açık girişler kapanır.
- qtyPct kalanın yüzde kaçının kapanacağını söyler. Verilmezse tamamı kapanır.
- Art arda verilen yüzde 50'ler kalanı her seferinde yarıya indirir; hiçbir zaman tam sıfırlamaz.
fromEntry'yi yanlış yazarsan hiçbir uyarı almazsın. Ad hiçbir açık girişle eşleşmezse çıkış hiçbir şey kapatmaz ve pozisyon veri bitene kadar açık kalır. Sessizce yanlış çalışan bir betiğin en sık sebebi budur.
exit işlevi
exit(condition, fromEntry?, qtyPct?, id?, limit?, stop?)
Değer döndürmez; piyasa çıkışı tetikler ya da adlı bir bekleyen çıkış emri kurar.
Hepsini kapatmak: closeAll
closeAll adres tanımaz. Koşul doğru olduğu barda açık pozisyonun tamamını kapatır: kademeli girişler, yarım kalmış parçalar, hepsi. Bir risk koşulu doğduğunda seçim yapmadan her şeyi bırakmak istediğin yerde kullanılır.
// asiri alim bolgesinde ne varsa birak enterLong(crossover(close, sma(close, 20))) closeAll(rsi(close, 14) > 75) plot(rsi(close, 14), "RSI", "#ff9800", 1)
Alt panoda turuncu RSI çizgisi görünür. Ortalama yukarı kesildikten sonraki barda pozisyon açılır; RSI 75'in üstüne çıktığı ilk barın ardından pozisyonun tamamı kapanır.
closeAll'a koşul vermezsen her barda kapatır. Böyle bir betikte hiçbir pozisyon bir bardan uzun yaşamaz ve sınama sonucu anlamsız çıkar.
closeAll işlevi
closeAll(kosul?)
Değer döndürmez; koşul doğru olan barda açık pozisyonun tamamını piyasadan kapatır.
Pozisyonu okumak
Bazen betiğin kendi durumunu bilmesi gerekir: şu an pozisyonda mıyım, ne kadarı açık? İki ayrı işlev iki ayrı soruya cevap verir. position yalnız yönü söyler: uzunsa +1, kısaysa -1, pozisyon yoksa 0. positionSize ise yönle birlikte büyüklüğü verir.
enterLong(crossover(close, sma(close, 20)), id='ana') exit(crossover(rsi(close, 14), 65), fromEntry='ana', qtyPct=50) exit(crossunder(close, sma(close, 20)), fromEntry='ana') plot(position(), "yon", "#2962ff", 1) plot(positionSize(), "kalan", "#ff9800", 1)
Alt panoda iki basamaklı çizgi olur. Mavi çizgi pozisyon boyunca 1'de durur ve çıkışta 0'a iner. Turuncu çizgi kısmi çıkış barında 1'den 0.5'e düşer; yön değişmediği hâlde büyüklüğün azaldığını orada görürsün.
- Yönü yalnızca süzgeç olarak kullanacaksan position() yeter.
- Kısmi çıkıştan sonra positionSize() kesirli olabilir; bu bir arıza değildir.
- Kademeli girişte positionSize() giriş sayısınca yükselir: üç giriş açıkken +3 olur.
positionSize işlevi
positionSize()
Yön ile açık girişlerin kalan büyüklüğünün çarpımı; kesirli olabilir. Pozisyon yoksa 0.
Aynı barda hem giriş hem çıkış
Koşullar birbirinden bağımsız yazıldığı için bir barda ikisinin birden doğru olması olağandır. Karar her zaman o anda taşınan pozisyona bakılarak verilir. Kuralı bilirsen grafik seni şaşırtmaz.
- Pozisyon yokken giriş kazanır. Çıkışın kapatacağı bir şey olmadığı için o satır sessizce düşer.
- Uzun pozisyondayken çıkış ile yeni bir uzun giriş birlikte doğarsa çıkış kazanır. Pozisyon kapanır, yerine yenisi açılmaz.
- Uzun pozisyondayken ters yön sinyali gelirse dönüş olur. Pozisyon kapanır ve aynı fiyattan kısa pozisyon açılır; arada boşta kalınan bar olmaz.
- Pozisyon yokken enterLong ile enterShort birlikte doğarsa uzun kazanır.
// ayni kosul hem girise hem cikisa bagli ort = sma(close, 20) kesis = crossover(close, ort) enterLong(kesis) exit(kesis) plot(ort, "ortalama", "#2962ff")
İlk kesişimde pozisyon açılır, çünkü o an kapatılacak bir şey yoktur. İkinci kesişimde pozisyon kapanır, üçüncüde yeniden açılır. Grafikte bir açılıp bir kapanan sıralı işlemler görürsün.
// ayni ortalamanin iki yonu: yukari kesiste uzun, asagi kesiste kisa ort = sma(close, 20) enterLong(crossover(close, ort)) enterShort(crossunder(close, ort)) plot(ort, "ortalama", "#2962ff") plot(positionSize(), "kalan", "#ff9800", 1)
Strateji hiç boşta kalmaz; her kesişimde yön değişir. Alt panodaki turuncu çizgi +1 ile -1 arasında gider gelir, aradan 0 geçmez. Kapanan uzun işlemin çıkış fiyatı, açılan kısa işlemin giriş fiyatıyla aynıdır.
Dönüş istemiyorsan yönü kısıtla. allowEntryIn ile tek yöne izin verirsen ters sinyal açık pozisyonu kapatır ama yeni pozisyon açmaz.
// yalniz uzun tarafa izin ver allowEntryIn("long") enterLong(crossover(close, sma(close, 20))) enterShort(crossunder(close, sma(close, 20))) plot(positionSize(), "kalan", "#ff9800", 1)
Aşağı kesiş artık kısa pozisyon açmaz, yalnız açık uzun pozisyonu kapatır. Alt panodaki çizgi 0 ile +1 arasında kalır, hiç eksiye inmez.
Geriye dönük sınama sonucunu okumak
Betiği çalıştırdığında terminal iki şey gösterir: grafikteki giriş-çıkış işaretleri ve bir özet. Önce işaretlere bak, sonra sayılara. İşaretler beklediğin yerde değilse sayının hiç önemi yoktur.
- Net kâr: başlangıç sermayesine göre yüzde kazanç ya da kayıp.
- İşlem sayısı: kapanan işlem adedi. Az işlemle çıkan yüksek oranlar rastlantı olabilir.
- Kazanma oranı: kârla kapanan işlemlerin yüzdesi.
- Kâr faktörü: toplam kârın toplam zarara oranı. 1'in altı, stratejinin para kaybettiği anlamına gelir.
- En büyük düşüş: sermayenin tepe noktasından ne kadar geri çekildiği. Dayanman gereken sayı budur.
Aynı sayıları betiğin içinden de okuyabilirsin. Bunları çizmek, sonucun hangi dönemde biriktiğini görmenin en hızlı yoludur.
// net kar-zarar egrisini alt panoda izle enterLong(crossover(close, sma(close, 10))) exit(crossunder(close, sma(close, 10))) plot(netPnl(), "net kar zarar", "#26a69a", 1)
Alt panoda sıfırdan başlayan bir eğri belirir. Yatay kaldığı yerlerde strateji pozisyonda değildir; sıçradığı yerler işlemlerin kapandığı barlardır.
// kapanan islem sayisi basamak basamak artar enterLong(crossover(close, sma(close, 10))) exit(crossunder(close, sma(close, 10))) plot(tradeCount(), "kapanan islem", "#787b86", 1)
Alt panoda hiç alçalmayan bir merdiven görünür. Her basamak bir işlemin kapandığı bardır. Merdiven çok dikse strateji fazla sık işlem yapıyor demektir.
- equity(): sermaye eğrisi.
- netPnl(): sermaye eksi başlangıç sermayesi.
- tradeCount(): o ana kadar kapanan işlem sayısı.
- winTrades() ve lossTrades(): kârla ve zararla kapananların adedi.
- drawdownPct(): sermayenin tepe noktasından şu an ne kadar geride olduğu.
Veri bittiğinde hâlâ açık bir pozisyon varsa o pozisyon son barın kapanışıyla kapatılır ve özete girer. Bu yüzden son işlemin çıkış fiyatı bir sinyalin değil, verinin bitişinin sonucudur.
Tek bir sembolde ve tek bir dönemde iyi çıkan sayı, stratejinin iyi olduğunu göstermez. Aynı betiği başka sembollerde ve başka zaman dilimlerinde de koştur. Sonuç ancak orada da ayakta kalırsa anlamlıdır.
Sık yapılan hatalar
- exit'i koşulsuz yazmak. İlk argüman zorunludur; boş bırakırsan betik hata verir.
- closeAll'ı koşulsuz bırakmak. Bu her barda kapatmak demektir ve pozisyon hiç yaşamaz.
- fromEntry'de girişin adını yanlış yazmak. Uyarı çıkmaz; çıkış sessizce hiçbir şey kapatmaz.
- Giriş işaretini sinyal barında beklemek. İşaret, emrin dolduğu bir sonraki barda çıkar.
- Sürekli doğru kalan bir koşulu giriş kuralı sanmak. close > sma(close, 20) yüzlerce barda doğrudur; dönüşün kendisini yakalamak için crossover kullan.
- position() ile büyüklük okumaya çalışmak. O yalnız +1, -1 ya da 0 döner; miktar için positionSize() gerekir.