RSI(5) ucuz hisseler stratejisi ve kümülatif momentum filtresi

Adnan Salih 13 Nisan 2017, 14:17

 

Dün ele aldığımız stratejide RSI(5) göstergesine göre en ucuz hisseleri alıp portföy getirisini incelemiştik.

Bugün stratejiyi kümülatif momentum göstergesi ile birleştirip filtrelediğimizde elde edeceğimiz sonuçların orijinal strateji ile karşılaştırmalı olarak analizini yapacağım.

Kodlamayı yine Matlab ortamında gerçekleştirdim.

Matlab için iyi bir kaynak:

https://goo.gl/uUs7ZV

Python başlangıç için iyi bir kaynak:

https://goo.gl/6TZQBf

Matlab neden elverişli?

Kod vektörize çalışır, başka programlarda satırlar boyu yapılan analiz sadece tek bir komutla gerçekleştirilebilir.

Hazır fonksiyonları vardır.

Süper bir web sitesi ve kod paylaşan on binlerce geliştirici mevcuttur.

Python neden elverişli?

Kütüphaneleri son zamanlarda çok gelişti.

Mükemmel bir paylaşım ortamı mevcut. Pek çok insan kodları paylaşıp daha iyileştirmeye çalışıyor.

Ücretsiz.

Çok hızlı.

Yazılım öğrenmek için iyi bir web sitesi:

https://www.w3schools.com/

Filtre kullanılmış RSI (5) testimize geçebiliriz.

Kümülatif momentum stratejisi

Al

½ daha al

Kısa yap

Şeklinde 3 sinyal üretiyordu. Aldığımız ve sattığımız pozisyonların göstergeye göre ortalamaya yakınlaştığı yerlerde de pozisyondan çıkıyorduk.

Kümülatif momentum yazıları köşede bulunabilir.

Bugünkü testler için 2 yeni bir fonksiyon yazmak gerekti, en sona aldım.

Kümülatif momentum trade fonksiyon girdilerini alıp kümülatif momentum göstergesini, pozisyonları, uzun pozisyonları ve kısa pozisyonları döndürecek.

Gelen pozisyonlara göre RSI (5) stratejisi:

Orijinal halinde

Sadece Long sinyali geldiğinde

Sadece Long Sinyali geldiğinde 2X kaldıraçlı

Short sinyali olmadığında

Çalıştırılacak.

Veri seti dün kullandığımız olacak. Test sonuçlarına bakalım:

Orijinal Strateji Yıllık ortalama getiri 0.3161 Sharpe  rasyosu: 1.1135

Karlı gün oranı 0.55

Zararlı gün oranı 0.44

Kar faktörü: 0.80

En büyük düşüş -0.6026 oldu. Bu kayıp: 571 gün sonra geri alındı

 

Long only Yıllık ortalama getiri 0.1912 Sharpe  rasyosu: 1.3352

En büyük düşüş -0.1856 oldu. Bu kayıp: 182 gün sonra geri alındı

 

2x Kaldıraçlı Long only Yıllık ortalama getiri 0.3112 Sharpe  rasyosu: 1.1215

En büyük düşüş -0.3765 oldu. Bu kayıp: 216 gün sonra geri alındı

 

Short Sinyali Yok İken Yıllık ortalama getiri 0.2903 Sharpe  rasyosu: 1.1420

En büyük düşüş -0.5505 oldu. Bu kayıp: 428 gün sonra geri alındı

Elapsed time is 12.651095 seconds.

Grafiklere de bakalım:

2016-17 yılı için grafikler:

Sonuç:

Strateji Long only / Short sinyali olmadığında pozisyonsuz günlere %8/365 günlük faiz ekledim. 2X kaldıraçlıda ise %12/365 günlük faiz düştüm.

Orijinal strateji tüm filtrelenmiş stratejilerden getiri olarak daha iyi

Sadece long sinyali olduğunda işlem yaparsak Sharpe rasyosu yükseliyor.

2X long yaptığımızda orijinal strateji ile aynı Sharpe rasyosunu elde ediyoruz. Getiri çok yaklaşıyor, portföy düşüşleri ise ciddi gerilemiş durumda.

Sadece short sinyali olmadığında işlem yapmak getirileri azaltıyor, Sharpe rasyosunda da anlamlı bir gelişim yok.

Kümülatif momentum filtresi ile işlem yapacaksak 2X long only riski sevenler için, esnek fon gibi çalışmak istenirse de sadece long sinyalinde işlem yapılabilir.

Strateji hareketli ortalama, volatilite vs. gibi filtrelere de tabi tutulabilir.

Kod değiştirilmiş haliyle aşağıda.

 

Kümülatif momentum için:

function out=kumulatifMomentum(closePx,regDays,stockNames)

regVec=(1:regDays)';

hisseSay=size(stockNames,2);

betaMat=zeros(size(closePx));

logPx=log(closePx);

for i=(regDays+1):size(closePx,1);

    for j=1:hisseSay

        betaMat(i,j)=exp(regcoeff2(regVec,logPx(i-regDays:i-1, j)))-1;

    end

out=smartmean(betaMat,2);


end

 

Kümülatif momentum işlemi için:

 

function [slope,numUnits,longUnits,shortUnits]=kumulMomentumTrade(closePx,regDays,stockNames,
longLevel,shortLevel,tradeClose,secondPos,longSecondClose,buffer,shortSecond,shortSecondClose)

%sistem 75 günlük regresyon ortalamasına göre işlem
%yapar. Long 1 + Long2 - Short 1 olarak çalışır
%short 2 denedim ama getirileri pozitif olarak etkilemedi.
%prensip ucuzken 1 tane al, pahalılandığında 1 tane daha al
%pahalı hale gelince sat, bir tane short yap

slope=kumulatifMomentum(closePx,regDays,stockNames);

%slope=slopemedyan;
longlevel=-longLevel/100;
shortlevel=shortLevel/100;
tradeclose=tradeClose/100;
%ikinci pozisyonlar için kriterler
secondpos=secondPos/100;
longSecondClose=longSecondClose/100;
buffer=buffer/100;

shortSecond=-shortSecond/100;
shortSecondClose=-shortSecondClose/100;

longsEntry = (slope longsExit= ( lag(slope)<tradeclose&slope>tradeclose );

%bir yerden yukarı keserse bir daha alalım alalım, belirli bir yerde kapatalım
longSecond=lag(slope)<secondpos&slope>secondpos;
longSecondExit=slope>longSecondClose|(((lag(slope)>(secondpos-buffer))&(slope<(secondpos-buffer))));


shortsEntry = ( slope>shortlevel);
shortsExit = ( slope<tradeclose) ;

shortSecond=lag(slope)>shortSecond&slope<shortSecond;
shortSecondExit=((lag(slope)<shortSecond)&slope>(shortSecond+buffer))|slope<shortSecondClose;

numUnitsLong=NaN(size(closePx,1),1);
numUnitsShort=NaN(size(closePx,1),1);
numUnitsLong2=NaN(size(closePx,1),1);
numUnitsShort2=NaN(size(closePx,1),1);


numUnitsLong(1)=0;
numUnitsLong(longsEntry)=1; %burada uzun pozisyon girişlerini 1 yapıyor
numUnitsLong(longsExit)=0;%uzun poz çıkışlar sıfır yapılıyor
%numdene=numUnitsLong;
%buydates=find(numdene==1);
%selldates=find(numdene==0);
numUnitsLong=fillMissingData(numUnitsLong);%takipeden günler dolduruluyor.
%short pozisyonlar için de yukarıdaki mantıkta iş yapılıyor.

numUnitsShort(1)=0;
numUnitsShort(shortsEntry)=-1;
numUnitsShort(shortsExit)=0;
numUnitsShort=fillMissingData(numUnitsShort);



%aşağıdaki blok kesişim sonrası alıyor kritere göre satıyor

numUnitsLong2(1)=0;
numUnitsLong2(longSecond)=1; %burada uzun pozisyon girişlerini 1 yapıyor
numUnitsLong2(longSecondExit)=0;%uzun poz çıkışlar sıfır yapılıyor
numUnitsLong2=fillMissingData(numUnitsLong2);%takipeden günler dolduruluyor.

numUnitsShort2(1)=0;
numUnitsShort2(shortSecond)=-1; %burada uzun pozisyon girişlerini 1 yapıyor
numUnitsShort2(shortSecondExit)=0;%uzun poz çıkışlar sıfır yapılıyor
numUnitsShort2=fillMissingData(numUnitsShort2);%takipeden günler dolduruluyor.



%short pozisyonlar için de yukarıdaki mantıkta iş yapılıyor.


numUnits=1.0*numUnitsLong+1.0*numUnitsShort+0.5*numUnitsLong2+numUnitsShort2;
shortUnits=numUnitsShort+numUnitsShort2;
longUnits=numUnitsLong+0.5*numUnitsLong2;

end

 

Değiştirilmiş Matlab kod:

clc,clear
tic
load('xu100kumulmom', 'Tarih','xu100Hisse', 'xu100cl');
%load('xu030LongShort', 'dates', 'xu100Hisse', 'xu100cl');

%Tarih=dates;
rsiThresholdUp=70;
rsiThresholdDown=30;
noOfStocks=3;
rsiDays=5;

[row,col]=size(xu100cl);

rsiVec=zeros(size(xu100cl));
rsi2Vec=zeros(size(xu100cl));

for i=1:col

    rsiVec(:,i)=rsindex(xu100cl(:,i),rsiDays);

end



rsiUp=sum(rsiVec>rsiThresholdUp,2);
rsiDown=sum(rsiVec<rsiThresholdDown,2);

%buradan itibaren ucuz rsi stratejisini yapacak

checkRsi=createind3(rsiVec);

%rsi hesaplandığı gün sayısından bir fazla olmalı

portfolioIndex=checkRsi(rsiDays+1:end-1,1:noOfStocks);

%hisse getirileri

stockReturns=log(xu100cl)-log(lag(xu100cl));

%rsi gününden 2 fazla olmalı. rsi ile bugün aldık, yarın sattık

returnsUsed=stockReturns(rsiDays+2:end,:);

[row2]=size(returnsUsed,1);

portRet=zeros(size(returnsUsed));

%getiriler içerisinden portföye aldığımız hisselerin getirilerini bulacağız

for ii=1:row2

    for jj=1:noOfStocks

        portRet(ii,jj)=returnsUsed(ii,portfolioIndex(ii,jj));

    end


end

%hisse isimlerini bulalım:

secilenHisse=num2cell(zeros(size(portfolioIndex)));

for idx=1:size(portfolioIndex,1)

     for idx2=1:noOfStocks

         secilenHisse(idx,idx2) = xu100Hisse(portfolioIndex(idx,idx2));

     end

 end

%kümülatif momentum trade fonksiyonuna aşağıdaki parametreler gönderilecek;
% longlevel=0.15
% shortlevel=0.25
% tradeclose=0.011
% %ikinci pozisyonlar için kriterler
% secondpos=0.11
% longSecondClose=0.20
% buffer=0.015
%
% shortSecond=10
% shortSecondClose=0.25
%[numUnits,longUnits,shortUnits]=kumulMomentumTrade(closePx,regDays,stockNames,longLevel,
shortLevel,tradeClose,secondPos,longSecondClose,buffer,shortSecond,shortSecondClose)

[slope,numUnits,longUnits,shortUnits]=kumulMomentumTrade(xu100cl,75,xu100Hisse,
0.15,0.25,0.011,0.11,0.20,0.015,10,25);

%portföy normal getiri:

portRet2=smartsum(portRet,2)/noOfStocks;

%sadece long sinyal geldiğinde işlem yaparsak port getirisi

portLongSignal=portRet2.*longUnits(rsiDays+2:end);
%2X kaldıraç olduğunda %12 faiz gideri koydum.

portLongSignal2X=(2*portRet2-0.12/365).*longUnits(rsiDays+2:end);

%portföyün pozisyonda olmadığı günleri bulalım:

findNoPosDays=find(longUnits(rsiDays+2:end)==0);

%bulduğumuz günlere faiz eklemek gerekir, yıllık %8 net yaptım.

portLongSignal(findNoPosDays)=0.08/365;
portLongSignal2X(findNoPosDays)=0.08/365;

%short sinyal olmadığı günleri belirleyelim:

tradeWhenNoShortSignal=numUnits>-1;

%short sinyal olmadığında getiriler

portLongSignal2=portRet2.*tradeWhenNoShortSignal(rsiDays+2:end);

%short sinyalinin olduğu günleri bulalım

findNoShortDays=find(shortUnits(rsiDays+2:end)==-1);

%faiz ekleyelim:

portLongSignal2(findNoShortDays)=0.08/365;

pozitifGun=sum(portRet2>0)/size(portRet2,1);
negatifGun=sum(portRet2<0)/size(portRet2,1);

pozitifGunler=portRet2>0;
negatifGunler=portRet2<0;

pozitifGunOrt=sum((pozitifGunler.*portRet2))/sum(portRet2>0);
negatifGunOrt=sum((pozitifGunler.*portRet2))/sum(portRet2<0);

karFaktoru=pozitifGunOrt/abs(negatifGunOrt);

%portföy istatistikleri

[apr,sharpe,maxDD,maxDDD,drawdown]=portMetrics(portRet2);

[apr2,sharpe2,maxDD2,maxDDD2,drawdown2]=portMetrics(portLongSignal);

[apr3,sharpe3,maxDD3,maxDDD3,drawdown3]=portMetrics(portLongSignal2);

[apr4,sharpe4,maxDD4,maxDDD4,drawdown4]=portMetrics(portLongSignal2X);

close all

%portföy grafikleri

figure

subplot(2,2,1)
plot(Tarih(rsiDays+1:end-1,:),cumprod(portRet2+1)-1)
title('Orijinal Strateji Portföy Getirisi')

subplot(2,2,2)
plot(Tarih(rsiDays+1:end-1,:),cumprod(portLongSignal+1)-1)
title('Sadece Long Sinyal Olduğunda İşlem')

subplot(2,2,3)
plot(Tarih(rsiDays+1:end-1,:),cumprod(portLongSignal2X+1)-1)
title('Sadece Long Sinyal Olduğunda 2X Kaldıraçlı İşlem')

subplot(2,2,4)
plot(Tarih(rsiDays+1:end-1,:),cumprod(portLongSignal2+1)-1)
title('Sadece Short Sinyal Olmadığında İşlem')

%2016-2017 yılı getirilerini çekip grafik yapacağız

ret2017=portRet2(end-325:end);
ret2017L=portLongSignal(end-325:end);
ret2017L2=portLongSignal2X(end-325:end);
ret2017S=portLongSignal2(end-325:end);
date2017=Tarih(end-325:end);


figure

%2016-17 getirileri
subplot(2,2,1)
plot(date2017,cumprod(ret2017+1)-1,'r')
title('2016-17 Orijinal Portföy Getirisi')

subplot(2,2,2)
plot(date2017,cumprod(ret2017L+1)-1,'r')
title('2016-17 Sadece Long Sinyalinde Portföy Getirisi')

subplot(2,2,3)
plot(date2017,cumprod(ret2017L2+1)-1,'r')
title('2016-17 2x Long Portföy Getirisi')

subplot(2,2,4)
plot(date2017,cumprod(ret2017S+1)-1,'r')
title('2016-17 Short Sinyal Olmadığında Portföy Getirisi')



% kümülatif rsi için grafikler

figure

subplot(1,2,1)
plot(Tarih(end-100:end),rsiUp(end-100:end))
hold on
plot(Tarih(end-100:end),rsiDown(end-100:end))
%
legend('Pozitif Trend','Negatif Trend')
title('2017 İlk Çeyrek')
%
%  %2016 aynı dönem
%
subplot(1,2,2)
plot(Tarih(1422:1510),rsiUp(1422:1510))
hold on
plot(Tarih(1422:1510),rsiDown(1422:1510))
legend('Pozitif Trend','Negatif Trend')
title('2016 İlk Çeyrek')

fprintf('Orijinal Strateji Yıllık ortalama getiri %.4f sharpe rasyosu: %.4f\n',apr,sharpe);
fprintf('Karlı gün oranı %.2f\n',pozitifGun);
fprintf('Zararlı gün oranı %.2f\n',negatifGun);
fprintf('Kar faktörü: %.2f\n',karFaktoru);
fprintf('En büyük düşüş %.4f oldu. Bu kayıp: %d gün sonra geri alındı\n\n',maxDD,maxDDD);

fprintf('Long only Yıllık ortalama getiri %.4f sharpe rasyosu: %.4f\n',apr2,sharpe2);
fprintf('En büyük düşüş %.4f oldu. Bu kayıp: %d gün sonra geri alındı\n\n',maxDD2,maxDDD2);

fprintf('2x Kaldıraçlı Long only Yıllık ortalama getiri %.4f sharpe rasyosu: %.4f\n',apr4,sharpe4);
fprintf('En büyük düşüş %.4f oldu. Bu kayıp: %d gün sonra geri alındı\n\n',maxDD4,maxDDD4);

fprintf('Short Sinyali Yok İken Yıllık ortalama getiri %.4f sharpe rasyosu: %.4f\n',apr3,sharpe3);
fprintf('En büyük düşüş %.4f oldu. Bu kayıp: %d gün sonra geri alındı\n',maxDD3,maxDDD3);

toc

Orijinal Strateji Yıllık ortalama getiri 0.3161 sharpe rasyosu: 1.1135
Karlı gün oranı 0.55
Zararlı gün oranı 0.44
Kar faktörü: 0.80
En büyük düşüş -0.6026 oldu. Bu kayıp: 571 gün sonra geri alındı

Long only Yıllık ortalama getiri 0.1912 sharpe rasyosu: 1.3352
En büyük düşüş -0.1856 oldu. Bu kayıp: 182 gün sonra geri alındı

2x Kaldıraçlı Long only Yıllık ortalama getiri 0.3112 sharpe rasyosu: 1.1215
En büyük düşüş -0.3765 oldu. Bu kayıp: 216 gün sonra geri alındı

Short Sinyali Yok İken Yıllık ortalama getiri 0.2903 sharpe rasyosu: 1.1420
En büyük düşüş -0.5505 oldu. Bu kayıp: 428 gün sonra geri alındı

Elapsed time is 12.671882 seconds.

Sayfada yer alan bilgiler tavsiye niteliği taşımayıp yatırım danışmanlığı kapsamında değildir. Yatırımcı profilinize uymayabilir.

Yorumlar

  • Can D.14 Nisan 2017 11:43Matlab ve Python dışında R dili ve finansal zaman serileri ile çalışmayı verimli hale getiren Quantmod paketini de öneririm. Pythonda bu işler için kullanılan pandas paketi zaten Cliff Asness'in AQR Capital'inde çalışan bir quant tarafından R'daki fonksiyon ve veri yapılarının Python'a aktarılmasından başka bir şey değildir. Python ve Matlab'ın R'a göre hızlı olması şehir efsanesidir. R'ın bazı fonksiyonları R'ın kendisi ve fortranda yazıldığı için performans düşüklüğü vardır ama hemen hemen her fonskisyonun tıpkı python ve matlabdaki gibi C++'da yazılmış birer alternatifi de vardır ve bunlar kullanılırsa, performans ciddi artmaktadır. Ayrıca işlemcinin bütün çekirdeklerini kullanabilen parallel processing libraryleri python ve matlab'dan daha basit olduğu için 4 çekirdekli basit bir makinede bile özellikle taramalarda (loop) 300x kat daha hızla çalışmaktadır.

    (%0,00) (%100,00)

Diğer Yazıları