Pensijų kaupimo bendrovių ir jų fondų palyginimas

Duomenys imami iš Lietuvos Banko svetainės.

Fondai lyginami pagal IRR rodiklį.

IRR (internal rate of return – vidinė grąžos norma) parodo, kiek per metus vidutiniškai paauga (procentais) kiekvienas iš atliktų SODRA pervedimų (investicijų) į pensijų fondus. IRR atsižvelgia į visus pensijų fondų atskaitymus bei į lėšų pervedimo laiką.

Palyginimui paimti duomenys 3 metų trukmės fondo veiklos laikotarpio.

In [3]:
%matplotlib inline
In [211]:
import os
import re
import collections
import numpy as np
import pandas as pd
import matplotlib as mpl
import requests
import bs4
import matplotlib.pyplot as plt
from IPython.display import display
In [5]:
mpl.rc('font', family='Ubuntu', size=14)
mpl.rc('figure', figsize=(16, 10))

Duomenų surinkimas ir valymas

Lietuvos bankas, duomenis apie pensijų fondus pateikia formatu, kurį sunku analizuoti, todėl, pirmiausia reikia susirinkti duomenis ir juos susitvarkyti, taip, kad būtų galima atlikti duomenų analizę.

Pirmiausiai parsisiunčiame visus ataskaitų failus:

In [7]:
resp = requests.get('https://www.lb.lt/ii_pakopos_pensiju_fondu_irr_rodiklis')
soap = bs4.BeautifulSoup(resp.content, 'lxml')
files = [x['value'] for x in soap.select('select.samewin option') if x['value'] != '#']
with open('urls', 'w') as f:
    for x in files:
        f.write('https://www.lb.lt/%s\n' % x)
!wget --quiet --input-file urls

Analizei bus naudojami šių ataskaitų duomenis:

In [8]:
!cat urls
https://www.lb.lt/irr_2016-06-30
https://www.lb.lt/irr_2016-03-31
https://www.lb.lt/irr_2015-12-31
https://www.lb.lt/irr_2015-09-30
https://www.lb.lt/irr_2015-06-30
https://www.lb.lt/irr_2015-03-31
https://www.lb.lt/irr_2014-12-31
https://www.lb.lt/irr_2014-09-30
https://www.lb.lt/irr_2014-06-30
https://www.lb.lt/irr_2014-03-31
https://www.lb.lt/irr_2013-12-31
https://www.lb.lt/irr_2013-08-31
https://www.lb.lt/irr_2013-05-31
https://www.lb.lt/irr_2012-12-31
https://www.lb.lt/irr_2012-08-31
https://www.lb.lt/irr_20120531
https://www.lb.lt/irr_20120229
https://www.lb.lt/irr_20110831
https://www.lb.lt/irr_20110331
https://www.lb.lt/irr_20110228
https://www.lb.lt/irr_20101231
https://www.lb.lt/irr_20100930
https://www.lb.lt/irr_20100331
https://www.lb.lt/irr_20091231
https://www.lb.lt/irr_20090615

Išvalome ir sujungiame visus duomenų failus:

In [272]:
sheet_names = {
    'irr_20090615': 'IRR',
}
clean = []
# Atidarome visus duomenų failus
data = [
    (x, pd.read_excel(x, sheetname=sheet_names.get(x, 0)))
    for x in os.listdir() if x.startswith('irr_')
]
for i, (filename, d) in enumerate(data):
    try:
        # Randame eilutę, kurioje yra 'IRR*' stulpelis.
        column_row = next(x.Index for x in d.itertuples() if 'IRR*' in x or 'IRR' in x)
        # Ataskaitos failo data.
        match = re.match('Suskaičiuota (.+) dienai', d.columns[0])
        if match:
            dt = pd.to_datetime(match.group(1))
        else:
            dt = pd.to_datetime(d.columns[1])
        # Keičiame lentelės stulpelių pavadinimus į eilutę, kurioje radom 'IRR*' stulpelį.
        d.columns = d.ix[column_row].values
        # Konvertuojame 'IRR*' stulpelį į skaitinį tipą.
        d['IRR'] = pd.to_numeric(d['IRR*'] if 'IRR*' in d else d['IRR'], errors='coerce')
        # Pridedame naują stulpelį į lentelę su ataskaitos data.
        d['Data'] = dt
        # Pašaliname visas eilutes iš lentelės, kurios neturi IRR reikšmės.
        d.dropna(subset=['IRR'], inplace=True)
        # Kai kuriose ataskaitose nepateikiama pensijų kaupimo bendrovė
        if 'Pensijų kaupimo bendrovė' not in d.columns:
            d['Pensijų kaupimo bendrovė'] = None
            d['Pensijų fondo pavadinimas'] = d['Fondas']
        # Iš lentelės pasirenkame mus dominančius stulpelius.
        d = d[['Pensijų kaupimo bendrovė', 'Pensijų fondo pavadinimas', 'Data', 'IRR']]
        # Išsisaugome išvalytę lentelę.
        clean.append(d)
    except:
        print(i)
        print(filename)
        display(d)
        raise
# Sujungiame visų ataskaitų lenteles į vieną lentelę.
d = pd.concat(clean)

Suvienodiname pasikeitusių bendrovių ir fondų pavadinimus:

In [273]:
renames = {
    'Danske konservatyvaus valdymo': 'Danske pensija',
    'Konservatyvaus valdymo Danske pensija': 'Danske pensija',
    
    'ERGO Life Insurance SE': 'UAB "ERGO Lietuva gyvybės draudimas"',
    'ERGO Balans': 'ERGO balans',
    'ERGO Konservatyvusis': 'ERGO konservatyvusis',
    
    # http://www.invaldalt.com/lt/main/naujienos/Kitos_naujienos?ID=1331
    'UAB "Finasta Asset Management"': 'UAB "INVL Asset Management"',
    'Finasta Konservatyvaus investavimo': 'INVL STABILO II 58+',
    'Finasta Aktyvaus investavimo': 'INVL MEDIO II 47+',
    'Finasta Racionalios rizikos': 'INVL EXTREMO II 16+',
    'Finasta Nuosaikus': 'INVL STABILO II 58+',
    'Finasta Augančio pajamingumo': 'INVL MEZZO II 53+',
    'Finasta Subalansuotas': 'INVL MEDIO II 47+',

    'UAB "MP Pension Funds Baltic"': 'UAB "INVL Asset Management"',
    'MP Stabilo II': 'INVL STABILO II 58+',
    'MP Medio II': 'INVL MEDIO II 47+',
    'MP Extremo II': 'INVL EXTREMO II 16+',

    'Invalda Konservatyvus': 'INVL STABILO II 58+',
    'Invalda Nuosaikus': 'INVL STABILO II 58+',
    'Invalda Augančio pajamingumo': 'INVL MEZZO II 53+',
    'Invalda Aktyvaus investavimo': 'INVL MEDIO II 47+',
    'Invalda Subalansuotas': 'INVL MEDIO II 47+',
    'Invalda Racionalios rizikos': 'INVL EXTREMO II 16+',

    'INVL aktyvaus investavimo': 'INVL MEDIO II 47+',
    'INVL racionalios rizikos': 'INVL EXTREMO II 16+',
}
def apply(x):
    if x:
        x = (
            x.strip().
            replace('Pensija', 'pensija').
            replace('Ekstra', 'ekstra').
            replace('DnB NORD', 'DNB').
            replace('DnB', 'DNB')
        )
        x = renames.get(x, x)
    return x
d['Pensijų kaupimo bendrovė'] = d['Pensijų kaupimo bendrovė'].apply(apply)
d['Pensijų fondo pavadinimas'] = d['Pensijų fondo pavadinimas'].apply(apply)

Atstatome bendrovių pavadinimus, lentelėse, kuriose jie nebuvo pateikti. Bendrovių pavadinimai atstatomi pagal fondų pavadinimus.

In [274]:
unique = lambda x: list(collections.OrderedDict.fromkeys(x))
bendroves = {x: unique(g['Pensijų fondo pavadinimas'].values) for x, g in d.groupby(['Pensijų kaupimo bendrovė'])}
fondai = {x: k for k, v in bendroves.items() for x in v}
d['Pensijų kaupimo bendrovė'] = d.apply(lambda x: (
    x['Pensijų kaupimo bendrovė']
    if x['Pensijų kaupimo bendrovė']
    else fondai[x['Pensijų fondo pavadinimas']]
), axis=1)

Galiausiai gauname išvalytą lenterę su duomenimis iš visų Lietuvos banko ataskaitų, kuri tarodo taip:

In [281]:
d.head()
Out[281]:
Pensijų kaupimo bendrovė Pensijų fondo pavadinimas Data IRR
4 UAGDPB "AVIVA Lietuva" Aviva Europensija 2014-06-30 0.0302
5 UAB "Danske Capital investicijų valdymas" Danske pensija 2014-06-30 0.0316
6 UAB "DNB investicijų valdymas" DNB pensija 1 2014-06-30 0.0257
7 UAB "ERGO Lietuva gyvybės draudimas" ERGO konservatyvusis 2014-06-30 0.0272
8 UAB "INVL Asset Management" INVL STABILO II 58+ 2014-06-30 0.0507

Transformuojame lentelę į tokį pavidalą, kuris leidžia palyginti IRR įvairiais pjūviais.

In [280]:
pivot = pd.pivot_table(d, index='Data', columns=['Pensijų kaupimo bendrovė', 'Pensijų fondo pavadinimas'], values='IRR')
pivot.ix[:3,:6]
Out[280]:
Pensijų kaupimo bendrovė UAB "Citadele investicijų valdymas" UAB "DNB investicijų valdymas"
Pensijų fondo pavadinimas Citadele pensija 1 Citadele pensija 2 DNB pensija 1 DNB pensija 2 DNB pensija 3 ERGO balans
Data
2009-06-15 NaN NaN 0.019374 0.001624 -0.020811 NaN
2009-12-31 NaN NaN 0.076045 0.161496 0.219199 NaN
2010-03-31 NaN NaN 0.034633 0.041592 0.041157 NaN

Pensijų kaupimo bendrovių palyginimas

Visų bendrovių, visų fondų vidutinis IRR

In [231]:
fig = plt.figure()
plt.fill_between(pivot.index, pivot.min(axis=1), pivot.max(axis=1), color='grey', alpha=0.15)
frame = pivot.groupby(axis=1, level=0).mean()
frame[frame.mean().sort_values(ascending=False).index].plot(grid=True, colormap='jet', figsize=(16, 12), ax=fig.axes[0])
plt.ylabel('IRR')
Out[231]:
<matplotlib.text.Text at 0x7f2c91b96f60>

Kievienos bendrovės fondų palyginimas

Žemiau pateikiamas sąrašas grafikų, kuriuose pavaizduoti kiekvienos bendrovės fondų IRR rodikliai, laike. Šviesiai pilkas fonas rodo geriausių ir blogiausių fondų IRR ribas, o tamsiai pilkas fonas parodo visų fondų vidurkio standartinio nuokrypio ribas.

In [261]:
names = pivot.groupby(axis=1, level=0).mean().mean().sort_values(ascending=False).index
fig, axes = plt.subplots(len(names), 1, figsize=(16, 10 * len(names)))
mean, std = pivot.mean(axis=1), pivot.std(axis=1)
for i, name in enumerate(names):
    axes[i].fill_between(pivot.index, pivot.min(axis=1), pivot.max(axis=1), color='grey', alpha=0.15)
    axes[i].fill_between(pivot.index, mean-std, mean+std, color='grey', alpha=0.15)
    pivot[name].plot(grid=True, ax=fig.axes[i])
    axes[i].set_ylabel('IRR')
    axes[i].get_legend().set_title(name)