CODESCRIPT

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.
NOT

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.

CODESCRIPT
// 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.

DİKKAT

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.

condition
koşul
Doğru olduğu barda uzun giriş emri verilir.
stopLoss
sayı
İsteğe bağlı. Girişe göre yüzde zarar-kes; 2 yazarsan yüzde 2 demektir.
takeProfit
sayı
İsteğe bağlı. Girişe göre yüzde kâr-al.
id
metin
İsteğe bağlı. Girişin sabit adı; çıkışı bu ada yöneltmek için kullanılır.
limit
sayı
İsteğe bağlı. Bekleyen alış fiyatı; fiyat buraya inince dolar.
stop
sayı
İsteğe bağlı. Bekleyen kırılım fiyatı; fiyat buraya çıkınca dolar. limit ile birlikte verilemez.

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.

CODESCRIPT
// 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.

DİKKAT

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.

condition
koşul
Doğru olduğu barda kısa giriş emri verilir.
stopLoss
sayı
İsteğe bağlı. Yüzde zarar-kes. Kısa pozisyonda bu seviye giriş fiyatının üstündedir.
takeProfit
sayı
İsteğe bağlı. Yüzde kâr-al. Kısa pozisyonda bu seviye giriş fiyatının altındadır.
id
metin
İsteğe bağlı. Girişin sabit adı; çıkışı bu ada yöneltmek için kullanılır.

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.

CODESCRIPT
// 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.

NOT

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.

CODESCRIPT
// 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.
DİKKAT

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.

condition
koşul
Doğru olduğu barda çıkış tetiklenir. Zorunludur.
fromEntry
metin
İsteğe bağlı. Yalnız bu adı taşıyan girişi kapatır.
qtyPct
sayı
İsteğe bağlı. Kalanın yüzde kaçı kapanacak. Verilmezse tamamı.
id
metin
İsteğe bağlı. Bekleyen çıkış emrinin adı; limit ya da stop verirken gerekir.
limit
sayı
İsteğe bağlı. Bekleyen kâr-al fiyatı.
stop
sayı
İsteğe bağlı. Bekleyen zarar-kes fiyatı. limit ile birlikte verilirse ikisi çift olur; biri dolunca diğeri iptal edilir.

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.

CODESCRIPT
// 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.

DİKKAT

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.

kosul
koşul
Kapatma koşulu; doğru olduğu barda pozisyonun tamamı kapanır. Verilmezse her bar kapatılı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.

CODESCRIPT
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.
CODESCRIPT
// 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.

CODESCRIPT
// 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.

İPUCU

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.

CODESCRIPT
// 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.

CODESCRIPT
// 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.

CODESCRIPT
// 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.
NOT

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.

DİKKAT

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.