Aikasarjojen kuvailua ja analysointia

Seuraavassa tarvitaan pandas-datareader -kirjastoa, joka ei kuulu Anacondan vakioasennukseen. Voit asentaa sen esimerkiksi seuraavasti:

  • Käynnistä Anaconda Navigator ja valitse Environments
  • Jos pandas-datareader ei ole Installed-listassa, niin siirry Not installed -listaan
  • Jos pandas-datareader ei löydy tästäkään listasta, niin päivitä lista (Update index)
  • Valitse pandas-datareader ja napsauta alareunasta Apply ja seuraa asennuksen vaiheita.
In [1]:
import pandas as pd
import matplotlib.pyplot as plt

# Tätä tarvitaan datan noutamiseksi Yahoon-palvelusta
# Varmista, että pandas-datareader on asennettu
import pandas_datareader.data as web

%matplotlib inline

# Tyyli vaikuttaa grafiikan ulkoasuun
# Käytettävissä olevat tyylit voit listata komennolla plt.style.available
plt.style.use('seaborn-whitegrid')

Datojen nouto

Elisan ja Telian kurssihistoriat voi noutaa Yahoo Finance -palvelusta. Voit googlata muiden osakkeiden nimiä. Esimerkiksi halulla 'yahoo finance kemira' löydät Kemiran nimen Yahoossa.

In [2]:
elisa = web.DataReader('ELISA.HE', start = '2015-1-1', data_source = 'yahoo')
telia = web.DataReader('TELIA1.HE', start = '2015-1-1', data_source = 'yahoo')
In [3]:
elisa
Out[3]:
High Low Open Close Volume Adj Close
Date
2015-01-02 22.870001 22.549999 22.610001 22.680000 206279.0 17.464106
2015-01-05 22.850000 22.309999 22.700001 22.400000 249378.0 17.248501
2015-01-07 23.139999 22.100000 22.500000 22.920000 531047.0 17.648911
2015-01-08 23.200001 22.760000 22.900000 22.830000 381383.0 17.579609
2015-01-09 23.120001 22.760000 22.809999 22.860001 305832.0 17.602713
... ... ... ... ... ... ...
2020-10-23 45.240002 44.689999 44.689999 44.849998 229445.0 44.849998
2020-10-26 44.959999 44.250000 44.799999 44.310001 297727.0 44.310001
2020-10-27 44.520000 43.669998 44.310001 43.709999 496645.0 43.709999
2020-10-28 43.970001 43.009998 43.700001 43.279999 467529.0 43.279999
2020-10-29 43.689999 42.750000 43.119999 42.840000 189548.0 42.840000

1463 rows × 6 columns

In [4]:
telia
Out[4]:
High Low Open Close Volume Adj Close
Date
2015-01-02 5.360 5.285 5.340 5.305 1039894.0 0.085169
2015-01-05 5.300 5.205 5.290 5.205 929121.0 0.083564
2015-01-07 5.300 5.220 5.240 5.245 860595.0 0.084206
2015-01-08 5.410 5.260 5.260 5.405 1001285.0 0.086775
2015-01-09 5.420 5.290 5.400 5.295 917754.0 0.085009
... ... ... ... ... ... ...
2020-10-23 3.512 3.438 3.438 3.477 1158621.0 3.477000
2020-10-26 3.512 3.428 3.451 3.432 1020943.0 3.432000
2020-10-27 3.453 3.395 3.449 3.402 734921.0 3.402000
2020-10-28 3.402 3.266 3.402 3.290 2140798.0 3.290000
2020-10-29 3.327 3.262 3.286 3.264 781689.0 3.264000

1463 rows × 6 columns

Viivakaavioita

In [5]:
elisa['Close'].plot()
Out[5]:
<matplotlib.axes._subplots.AxesSubplot at 0x23b9264a790>
In [6]:
telia['Close'].plot()
Out[6]:
<matplotlib.axes._subplots.AxesSubplot at 0x23b923384c0>
In [7]:
# Vuoden 2020 alusta
elisa['Close']['2020':].plot()
Out[7]:
<matplotlib.axes._subplots.AxesSubplot at 0x23b923e7580>
In [8]:
telia['Close']['2020'].plot()
Out[8]:
<matplotlib.axes._subplots.AxesSubplot at 0x23b925bb190>

Aggregointi

resample() aggregoi aikasarjan esimerkiksi päivätasolta kuukausitasolle.

resample()-toiminnon parametrina käytettäviä arvoja:

https://pandas.pydata.org/pandas-docs/stable/user_guide/timeseries.html#offset-aliases

In [9]:
# Kuukausittaiset keskiarvohinnat
elisa['Close'].resample('M').mean().plot()
Out[9]:
<matplotlib.axes._subplots.AxesSubplot at 0x23b92bef580>
In [10]:
# Vuosineljännesten keskiarvohinnat
elisa['Close'].resample('Q').mean().plot()
Out[10]:
<matplotlib.axes._subplots.AxesSubplot at 0x23b92c865b0>
In [11]:
# Vuosien keskiarvohinnat
elisa['Close'].resample('Y').mean().plot()
Out[11]:
<matplotlib.axes._subplots.AxesSubplot at 0x23b93cca460>
In [12]:
# Vaihdon määrä osakkeiden lukumääränä vuosineljänneksittäin
elisa['Volume'].resample('Q').sum().plot()
Out[12]:
<matplotlib.axes._subplots.AxesSubplot at 0x23b93d205e0>

Liukuvia keskiarvoja

Liukuvilla keskiarvoilla tasoitetaan yksittäisiin ajankohtiin liittyviä satunnaisia piikkejä.

Teknisessä analyysissä aikasarjan ja liukuvien keskiarvojen leikkauskohtia käytetään osto- ja myyntisignaaleina.

In [13]:
elisa['Close'].plot()

# Lisätään viivakaavioon 50 päivän ja 200 päivän liukuvat keskiarvot
elisa['Close'].rolling(50).mean().plot()
elisa['Close'].rolling(200).mean().plot()
Out[13]:
<matplotlib.axes._subplots.AxesSubplot at 0x23b93d91f40>

Muutosprosentit

Muutosprosentit on kätevää laskea pct_change()-funktiolla.

In [14]:
# Hinnan muutokset prosentteina edellisestä päivästä
elisa['Elisa%'] = elisa['Close'].pct_change()
telia['Telia%'] = telia['Close'].pct_change()
In [15]:
# Yhdistän Elisan ja Telian muutosprosentit samaan dataframeen
muutokset = pd.concat([elisa['Elisa%'], telia['Telia%']], axis = 1)
muutokset
Out[15]:
Elisa% Telia%
Date
2015-01-02 NaN NaN
2015-01-05 -0.012346 -0.018850
2015-01-07 0.023214 0.007685
2015-01-08 -0.003927 0.030505
2015-01-09 0.001314 -0.020352
... ... ...
2020-10-23 0.003580 0.011638
2020-10-26 -0.012040 -0.012942
2020-10-27 -0.013541 -0.008741
2020-10-28 -0.009838 -0.032922
2020-10-29 -0.010166 -0.007903

1463 rows × 2 columns

In [16]:
# Muutokset vuoden alusta
ax1 = muutokset['2020':].plot()

ax1.set_ylabel('Muutos')
Out[16]:
Text(0, 0.5, 'Muutos')
In [17]:
# Tunnuslukuja muutosprosenteille
muutokset.describe()
Out[17]:
Elisa% Telia%
count 1462.000000 1462.000000
mean 0.000550 -0.000226
std 0.015215 0.014540
min -0.092226 -0.134499
25% -0.006522 -0.007110
50% 0.000750 -0.000494
75% 0.007916 0.007417
max 0.164016 0.109976
In [18]:
# Päivät, jolloin muutosprosentti ollut suurempi kuin 5 %
muutokset[(abs(muutokset['Elisa%']) > 0.05) | (abs(muutokset['Telia%']) > 0.05)]
Out[18]:
Elisa% Telia%
Date
2015-03-27 -0.075697 -0.012712
2015-04-09 0.011499 -0.066554
2015-04-16 0.055984 0.006346
2015-08-24 -0.055172 -0.058611
2015-08-25 0.065693 0.047335
2015-09-03 0.057869 0.032323
2015-09-25 0.061796 0.025510
2016-01-22 0.051540 0.037919
2016-04-01 -0.052092 -0.009645
2016-06-27 -0.046049 -0.079728
2017-04-07 -0.055222 -0.006459
2017-10-18 -0.053899 -0.006816
2018-01-31 0.058060 0.013300
2018-04-20 -0.001125 0.083107
2018-07-13 -0.092226 -0.009455
2018-10-18 -0.074751 -0.000496
2018-11-05 0.057805 0.014457
2019-01-25 -0.009144 -0.050542
2019-01-31 -0.051635 -0.008364
2019-04-04 -0.063350 -0.007843
2019-10-17 0.068757 -0.062849
2020-03-09 -0.040785 -0.064356
2020-03-12 -0.084077 -0.134499
2020-03-17 0.164016 0.109976
2020-03-18 0.055873 -0.061963
2020-03-19 0.036694 0.051014
2020-04-03 -0.076739 -0.066333
2020-04-22 0.059656 -0.006086
2020-06-16 0.022036 0.057786
2020-10-16 -0.061020 0.005671

Onko viikonpäivällä yhteyttä tuottoprosenttiin?

In [19]:
# Viikonpäivät omaan sarakkeeseen (0 = maanantai)
muutokset['Weekday'] = muutokset.index.weekday

muutokset.groupby('Weekday')['Elisa%'].describe()
Out[19]:
count mean std min 25% 50% 75% max
Weekday
0 290.0 0.001239 0.013350 -0.055172 -0.005331 0.000846 0.008036 0.057805
1 296.0 0.001150 0.016284 -0.038976 -0.007304 0.000389 0.007790 0.164016
2 298.0 0.000899 0.013463 -0.053899 -0.005736 0.000142 0.008159 0.059656
3 294.0 0.000040 0.015977 -0.084077 -0.007434 0.000875 0.008037 0.068757
4 284.0 -0.000615 0.016718 -0.092226 -0.006447 0.001064 0.007594 0.061796
In [20]:
muutokset.groupby('Weekday')['Telia%'].describe()
Out[20]:
count mean std min 25% 50% 75% max
Weekday
0 290.0 -0.000180 0.014720 -0.079728 -0.007218 -0.000661 0.007756 0.041424
1 296.0 0.000936 0.014820 -0.046268 -0.006549 -0.000493 0.007406 0.109976
2 298.0 -0.000239 0.012483 -0.061963 -0.006309 -0.000515 0.007552 0.042407
3 294.0 -0.001603 0.016225 -0.134499 -0.008193 -0.000494 0.006652 0.051014
4 284.0 -0.000043 0.014202 -0.066333 -0.007080 0.000000 0.007682 0.083107

Muutosprosenttien välinen korrelaatio

In [21]:
# Elisan ja Telian muutosprosentit korreloivat positiivisesti
muutokset.drop('Weekday', axis = 1).corr()
Out[21]:
Elisa% Telia%
Elisa% 1.000000 0.492025
Telia% 0.492025 1.000000
In [22]:
# Muutosprosenttien positiivinen korrelaatio näkyy hyvin hajontakaaviossa
muutokset.plot.scatter(x = 'Elisa%', y = 'Telia%')
Out[22]:
<matplotlib.axes._subplots.AxesSubplot at 0x23b93ee7b80>
In [23]:
# Liukuva korrelaatio kertoo miten muutosprosentit korreloivat eri aikoina
muutokset['Elisa%'].rolling(100).corr(muutokset['Telia%']).plot()
Out[23]:
<matplotlib.axes._subplots.AxesSubplot at 0x23b93cbf460>

Liukuva volatiliteetti

Volatiliteetti kuvaa osakkeeseen liittyvää riskiä.

Volatiliteetti voidaan laska päivittäisten muutosprosenttien keskihajontana ja se skaaltaaan vuositasolle kertomalla vuoden kaupantekopäivien lukumäärän neliöjuurella.

Liukuva volatiliteetti kuvaa, miten volatiliteetti on muuttunut ajan kuluessa.

In [24]:
# 252 päivän liukuva volatiliteetti
(muutokset['Elisa%'].rolling(252).std() * (252**0.5)).plot(label = 'Elisa', legend = True)
(muutokset['Telia%'].rolling(252).std() * (252**0.5)).plot(label = 'Telia', legend = True)
Out[24]:
<matplotlib.axes._subplots.AxesSubplot at 0x23b93fbdcd0>

Kahden arvoakselin viivakaavio

In [25]:
# Viivakaavio Elisan päätöshinnoista (axe)
axe = elisa['Close'].plot(color = 'C0')

# Elisan nimi, väri ja fonttikoko
axe.set_ylabel('Elisa', color = 'C0', fontsize = 14)

# Elisan arvoakselin skaalaus
axe.set_ylim(20, 60)

# Luon Telialle toisen axes-olion (axt), jolla on yhteinen x-akseli axe-olion kanssa
axt = axe.twinx()

# Viivakaavio Telian päätöshinnoista
telia['Close'].plot(ax = axt, color = 'C1')

# Telian nimi, väri ja fonttikoko
axt.set_ylabel('Telia', color = 'C1', fontsize = 14)

# Telian arvoakselin skaalaus
axt.set_ylim(3, 7)
Out[25]:
(3.0, 7.0)