Monte Carlo ile Petrol fiyat tahmini¶Barış Sanlı , barissanli.com Petrol fiyatlarını sayısal yöntemlerle tahmin etmek mümkün deÄŸil ama bazı dönemsel hareketlerin mekaniÄŸini bilmek faydalı olabilir. AÅŸağıdaki kod dizisinde, öncelikle 2020'nin ilk çeyreÄŸinin neden farklı bir dönem olduÄŸuna bakıyoruz, daha sonra tarihsel dönemleri örnek alırsak petrol fiyatlarının gidebileceÄŸi yönleri görmeye çalışıyoruz. Yapay zeka ile tabii ki 5-6 satırda bir tahmin yapılabilir. Ama biraz daha mantık setini anlamak için böyle bir model faydalı olabilir. Bölüm 1 - İstatistiksel Bakış¶Ã–nce gerekli kütüphanelerimizi yükleyelim.. Alttaki kod ile de grafikleri daha büyük görmek için gerekli olan dizilimi görmekteyiz In [1]:
%pylab inline
import pandas as pd
import seaborn as sb
In [2]:
plt.rcParams['figure.figsize'] = [10, 7] #grafikleri daha büyütmek için
Verilerimizi her Çarşamba güncellenen ABD Enerji Bakanlığı spot Brent fiyatlarından alacağız. Bu veriler Bloomberg-Reuters'de gördüğünüz verilerden farklı, çünkü spot fiyatlar. TV ekranlarında gördüğünüz ise genelde gelecek ay, yani teslimi 30-40 gün içinde yapılacak ürünler. In [3]:
link='https://www.eia.gov/dnav/pet/hist_xls/RBRTEd.xls'
Şimdi verimizi yükleyelim, "Data 1" çalışma sayfasını seçip, verinin başladığı satırdan veriyi okuyalım In [4]:
data = pd.read_excel(link,'Data 1', skiprows=(0,1))
İlk çeyrek verileri 1,2,3.ay verileri olduğundan, 2020 yılının ilk 3 ayının verilerini tüm veri setinden ayıralım In [5]:
data_s=data[(data["Date"].dt.year==2020) & (data["Date"].dt.month<4) ] #2020 ilk çeyrek
Şimdi de bu verimizin histogram-yani istatistiksel dağılımını çıkaralım In [6]:
hist(data_s["Europe Brent Spot Price FOB (Dollars per Barrel)"].pct_change().dropna(), bins=20);
Peki 2020 daha önceki yıllardan ne kadar farklıydı. Şimdi de 2020 öncesi yılların ilk çeyrek verilerini çıkaralım In [7]:
data_yil=data[(data["Date"].dt.year<2020) & (data["Date"].dt.month<4) ] #2020'den önceki yılların ilk çeyrekleri
Şimdi 2020 yılı ilk çeyrek ve 2020 yılı öncesindeki yılların ilk çeyrek değişim oranlarını çizdirelim In [8]:
sb.distplot(data_yil["Europe Brent Spot Price FOB (Dollars per Barrel)"].pct_change().dropna(), hist=False, rug=False,hist_kws={"range": [0,1]})
sb.distplot(data_s["Europe Brent Spot Price FOB (Dollars per Barrel)"].pct_change().dropna(), hist=False, rug=False, hist_kws={"range": [0,1]})
#sb.title("2020 yılı ve daha önceki yıllarda ilk çeyrek petrol fiyatı değişim oranı")
plt.show()
Bölüm 2 - Fiyat seviyelerine göre artış oranı mı yüzdesi mi?¶Mesela petrol fiyatları daha düşük iken, petrol artışları % olarak daha büyük olabiliyor ÅŸimdi bunu incelemeye çalışalım. Önce yeni bir sütun açarak yüzde deÄŸiÅŸim oranlarını yerleÅŸtirelim In [9]:
data["pct"]=data["Europe Brent Spot Price FOB (Dollars per Barrel)"].pct_change()
Fiyat seviyelerine göre fiyat değişim % oranlarına, yarı saydam noktalar bütünü olarak bakalım In [10]:
scatter(data["Europe Brent Spot Price FOB (Dollars per Barrel)"],data.pct,alpha=0.1)
Out[10]:
Şimdi ise yukarıdaki grafikte daha dar bir alana göz atalım In [11]:
scatter(data["Europe Brent Spot Price FOB (Dollars per Barrel)"],data.pct,alpha=0.5)
ylim(-0.03,0.03)
Out[11]:
Yukarıda dikkat ettiminiz mi bilmiyorum ama özellikle belirli fiyat seviyelerinde bir kümelenme var. Mesela 20$,40-80$ ve 110$ Veri satırımızda % değişimler için pct sütununu açmıştık. Şimdi de mutlak farklar için "dif" sütununu açalım In [12]:
data["dif"]=data["Europe Brent Spot Price FOB (Dollars per Barrel)"].diff()
Değişik fiyat seviylerindeki oynamanın tam sayı değerlerini kıyasladığımızda da benzer bir kesikli gruplama görüyoruz In [13]:
scatter(data["Europe Brent Spot Price FOB (Dollars per Barrel)"],data["dif"],alpha=0.2)
Out[13]:
Bir öncekindeki gibi biraz daha yakından bakalım In [14]:
scatter(data["Europe Brent Spot Price FOB (Dollars per Barrel)"],data["dif"],alpha=0.2)
ylim(-3,3)
Out[14]:
Bölüm 3 - Monte Carlo ile 2020 ilk dönemindeki değişimi tahmin¶2020 yılının ilk 3 ayının verileri data_s deÄŸiÅŸkenindeydi. Åžimdi bu dönemdeki yüzde deÄŸiÅŸimleri (pct_change) ile hesaplayarak, sayı olmayan sonuçları çıkaralım (dropna). Hesaplanan verileri de bir listeye çevirerek (.values) ile lst deÄŸiÅŸkenine alalım. In [15]:
#ilk çeyrekte fiyat değişim oranı
lst=data_s["Europe Brent Spot Price FOB (Dollars per Barrel)"].pct_change().dropna().values
Ben bir kaç seferde aşağıdaki kodu yazdığımdan kısaca ne yaptığını özetleyeyim
In [16]:
pricematrix=[]
startprice=67.7
days=90
numsim=300
for ax in np.arange(numsim):
sprice=startprice
price=[]
lsta=random.choice(lst,size=days)
for ai in np.arange(days):
sprice=sprice*(1+lsta[ai])
price.append(sprice)
pricematrix.append(price[:])
pmid=[]
pmin=[]
pmax=[]
npm=np.array(pricematrix)
for ax in np.arange(days):
pmax.append(np.amax(npm[0:numsim,ax]))
pmin.append(np.amin(npm[0:numsim,ax]))
pmid.append(np.average(npm[0:numsim,ax]))
for ax in np.arange(numsim):
plot(npm[ax,:], alpha=0.1*ax/numsim, color="red");
plot(pmax, color="#AA0000")
plot(pmin, color="#AA0000")
plot(pmid, color="#220000", linewidth=5)
Out[16]:
Görüldüğü üzere 2020 1.çeyrekteki deÄŸiÅŸim oranlarını ne kadar rastgele hale getirirsek getirelim en iyi ihtimalle bile fiyat düşüşü kaçınılmaz oluyor. Gerçekten de çok kötü bir dönem. Fiyat hatta spot olarak 20 dolar/varil in altına iniyor ki, bu deÄŸere 8-10 dolar/varil ekleyerek ekranda gördüğümüz deÄŸerlere de ulaÅŸabilirsiniz Bölüm 4 - Monte Carlo fonksiyonu¶Åžimdi de bu yaptığımız çalışmayı daha sistematik hale getirmek için bir fonksiyona dönüştürelim. Fonksiyonumuzda
In [17]:
def montecarlo(sp, days, lstx, numsim=300):
pricematrix=[]
days=days
for ax in np.arange(numsim):
sprice=sp
price=[]
lsta=random.choice(lstx,size=days)
for ai in np.arange(days):
sprice=sprice*(1+lsta[ai])
if (sprice<5): sprice=5
price.append(sprice)
pricematrix.append(price[:])
pmid=[]
pmin=[]
pmax=[]
pext=[]
npm=np.array(pricematrix)
for ax in np.arange(days):
pmax.append(np.amax(npm[0:numsim,ax]))
pmin.append(np.amin(npm[0:numsim,ax]))
pmid.append(np.average(npm[0:numsim,ax]))
pext.append(((np.amax(npm[0:numsim,ax])+np.amin(npm[0:numsim,ax]))/2))
for ax in np.arange(numsim):
plot(npm[ax,:], alpha=0.1*ax/numsim, color="red");
plot(pmax, color="#AA0000")
plot(pmin, color="#AA0000")
plot(pext, color="blue")
plot(pmid, color="#220000", linewidth=5)
print ("Ortalama=",pmid[days-1], " Uçların ortalaması=",pext[days-1])
return pmid[:]
#ylim(0,sp+50)
Fonkiyonumuzu test edelim, 67.7 dolardan başlarsak, 90 gün sonra simulasyonumuzdaki fiyat aralığı ne olur? In [18]:
montecarlo(67.7, 90, lst);
Bölüm 5 - 2020 yılı İkinci çeyrek için fiyat tahmini¶GeçtiÄŸimiz çeyrekteki deÄŸiÅŸimler ile petrol fiyatlarını Ocak ayından aldığımızda ne kadar simulasyon yaparsak yapalım fiyatların düştüğünü gördük. Burada en önemli sorun "ÅŸiÅŸman kuyruklar". Evet petrol fiyat deÄŸiÅŸimleri ortalama dağılıyor gibi gözükse de, 50 seçimden birinde %10 düşüş büyük bir yıkım oluyor. Sonuçlarda bu rahatlıkla görülüyor. Åžimdi de 2020 yılından önceki yıllardaki 4,5,6.aylardaki deÄŸiÅŸim oranları ile MonteCarlo simulasyonumuzu çalıştıralım. AÅŸağıda deÄŸiÅŸik dönemler için de filtreleme kodları # ile verilmiÅŸtir In [19]:
# 2.çeyrek
data_tmp=data[(data["Date"].dt.year<2020) & (data["Date"].dt.month>3) & (data["Date"].dt.month<7) ]
# 3.çeyrek
# data_tmp=data[(data["Date"].dt.year<2020) & (data["Date"].dt.month>6) & (data["Date"].dt.month<10) ]
# 4. çeyrek
# data_tmp=data[(data["Date"].dt.year>2010) & (data["Date"].dt.month>9) ]
# data_tmp=data[(data["Date"].dt.year>2016) & (data["Date"].dt.month>0) ]
#data_tmp=data[(data["Date"].dt.month>3) & (data["Date"].dt.month<5) ]
lst_tmp=data_tmp["Europe Brent Spot Price FOB (Dollars per Barrel)"].pct_change().dropna().values
28 dolardan başlatarak, 90 gün ileri doğru, 500 defa simulasyon yapalım, ortalama fiyatları da rslt listesine kopyalayalım In [20]:
rslt=montecarlo(28, 90 , lst_tmp[:], 500);
Peki ortalama olarak fiyatlar nereye gidiyormuş bir de onu görelim. Simulasyonu spot fiyatlar üzerinden yaptık. 13'ünde Brent spot 20 dolardan kapanmış, ama o gün gelecek fiyatı 30 dolar civarında. Yani 10 dolar da spottan gelecek kontrata bir contango olduğu için ekleme yapabiliriz. In [21]:
plot(rslt)
Out[21]:
In [22]:
sb.distplot(data_tmp["Europe Brent Spot Price FOB (Dollars per Barrel)"].pct_change().dropna(), hist=False, rug=False, hist_kws={"range": [0,1]})
Out[22]:
Bölüm 6 - Fiyat seviyelerine özel yüzde değişim ve seviye değişimi var ise¶Åžimdi ise bir baÅŸka teoriye bakalım. Petrol fiyatı bir seviyeye geldiÄŸinde -mesela 20 dolar'a-, deÄŸiÅŸim oranı o seviyedeki deÄŸiÅŸim oranlarına benzeÅŸiyor olabilir. Bu sebeple bu sefer biraz daha farklı bir yöntem uygulayacağız. Önce deÄŸiÅŸim ve farkları bir satır yukarı kaydıralım. Sebebi de basit petrol 20 dolardan 22 dolara çıktı ise %10 artış oranı olması fiyatın 22 deÄŸil 20 dolarda olmasına baÄŸlı. Ama fonksiyon bunu deÄŸiÅŸen miktar yani 22 ile aynı satıra basıyor. In [23]:
data["pct"]=data["pct"].shift(-1)
data["dif"]=data["dif"].shift(-1)
Sürekli tırnak içine uzun uzun "Europe " vs yazmaktan usandığım için işime yarayan verileri data2 isimli ayrı bir veri noktasına alıyorum ve uzun isimli sütunun adını price olarak değiştiriyorum In [24]:
data2=data[["Europe Brent Spot Price FOB (Dollars per Barrel)","pct","dif"]]
In [25]:
data2=data2.rename(columns={"Europe Brent Spot Price FOB (Dollars per Barrel)":"price"})
Verimize bir bakalım In [26]:
data2.head()
Out[26]:
Şimdi de verimizi en küçükten en büyüğe sıralayalım In [27]:
data2=data2.sort_values(by=['price'])
In [28]:
data2.head()
Out[28]:
Kod nasıl çalışıyor? Mesela başladığımız fiyat 60 dolar olsun. Fiyat hareketlerinin 50-70 dolar arasındaki kümesinden rastgele bir artış oranını aşağıdaki şekilde alıyoruz. Yani burada her fiyat seviyesinde o seviyeye özgü bir değişim oranı varmış gibi yaklaşıyoruz In [29]:
targetprice=60
100*random.choice(data2[(data2.price>(targetprice-10)) & (data2.price<(targetprice+10))].pct.values)
Out[29]:
Şimdi yukarıdaki MonteCarlo kodumuzu önce her fiyat seviyesinde benzer bir mutlak fiyat hareketi varmış gibi çalıştıralım. Yani yüzde değişim değil "dif" yani farklara bakalım In [30]:
startprice=28
days=30
numsim=500
pricematrix=[]
days=days
for ax in np.arange(numsim):
sprice=startprice
price=[]
for ai in np.arange(days):
arr=data2[(data2.price>(sprice-5)) & (data2.price<(sprice+5))].dropna()
change=random.choice(arr["dif"].values)
# change=average(arr["diff"].values)
sprice=sprice+change
price.append(sprice)
pricematrix.append(price[:])
In [31]:
pmid=[]
pmin=[]
pmax=[]
pext=[]
npm=np.array(pricematrix)
for ax in np.arange(days):
pmax.append(np.amax(npm[0:numsim,ax]))
pmin.append(np.amin(npm[0:numsim,ax]))
pmid.append(np.average(npm[0:numsim,ax]))
pext.append(((np.amax(npm[0:numsim,ax])+np.amin(npm[0:numsim,ax]))/2))
for ax in np.arange(numsim):
plot(npm[ax,:], alpha=0.1*ax/numsim, color="red");
plot(pmax, color="#AA0000")
plot(pmin, color="#AA0000")
plot(pext, color="blue")
plot(pmid, color="#220000", linewidth=5)
print ("Ortalama=",pmid[days-1], " Uçların ortalaması=",pext[days-1])
Görüldüğü üzere böyle bir durumda fiyat değişimlerinde ortalamada daha farklı sonuçlar elde ediliyor. Peki bir de % değişimlere baksak neler oluyor ona bakalım. Başlangıç fiyatımızı 28 dolardan ileri doğru 30 gün 500 defa simulasyonumuzu tekrarlayalım In [32]:
startprice=28
days=30
numsim=500
pricematrix=[]
days=days
for ax in np.arange(numsim):
sprice=startprice
price=[]
for ai in np.arange(days):
arr=data2[(data2.price>(sprice-5)) & (data2.price<(sprice+5))].dropna()
change=random.choice(arr["pct"].values)
# change=average(arr["diff"].values)
sprice=sprice*(1+change)
price.append(sprice)
pricematrix.append(price[:])
pmid=[]
pmin=[]
pmax=[]
pext=[]
npm=np.array(pricematrix)
for ax in np.arange(days):
pmax.append(np.amax(npm[0:numsim,ax]))
pmin.append(np.amin(npm[0:numsim,ax]))
pmid.append(np.average(npm[0:numsim,ax]))
pext.append(((np.amax(npm[0:numsim,ax])+np.amin(npm[0:numsim,ax]))/2))
for ax in np.arange(numsim):
plot(npm[ax,:], alpha=0.1*ax/numsim, color="red");
plot(pmax, color="#AA0000")
plot(pmin, color="#AA0000")
plot(pext, color="blue")
plot(pmid, color="#220000", linewidth=5)
print ("Ortalama=",pmid[days-1], " Uçların ortalaması=",pext[days-1])
|