Fund pricing data I got from inwestinfo.pl website and indicies prices from stooq.pl.

!pip install bs4 pandas fastcore seaborn

import datetime
import pandas as pd
import numpy as np
import requests
from bs4 import BeautifulSoup
import urllib3
urllib3.disable_warnings() # disable ssl verification warnings
from fastcore.parallel import parallel
from functools import reduce
import seaborn as sns

Getting the data

start_date = datetime.datetime.strptime('1995-09-01', '%Y-%m-%d').date()

def dates_since(start_date):
    d = start_date
    while d < datetime.date.today():
        yield d
        d += datetime.timedelta(days=1)

def get_fund_prices_for_day(day):
    print(".", end="")
    # stock funds from date d
    url = f'https://www.inwestinfo.pl/fundusze/fundusze-inwestycyjne/wszystkie/?funduszeKategorie%5B%5D=10&archiwum=1&dataArchiwum={day}'
    res = requests.get(url, verify=False) # do not verify the certificate
    
    quotes = []
    
    soup = BeautifulSoup(res.content, 'html.parser')
    
    try:
        table = soup.find(class_='table-data')
        table = table.find('tbody')
        for row in table.find_all('tr'):
            cells = row.find_all('td')
            name = cells[2].get_text()
            date = cells[3].get_text()
            price = cells[4].get_text()

            quotes += [(name, date, price)]
    except: # if parsing doesn't go as planned (for example no quotes for that day)
        pass

    return quotes

print("Getting fund pricing data ...", end="")

quotes = parallel( # run in parallel to speed it up
                get_fund_prices_for_day, 
                dates_since(start_date), 
                n_workers=16, 
                progress=False
)
quotes = reduce(lambda q1, q2: q1 + q2, quotes) # drop unnecessary dimension

df = pd.DataFrame.from_records(quotes, columns=['name', 'date', 'price'])
df['date'] = pd.to_datetime(df['date'])
df['price'] = df.price.astype(np.double)

print("Done")
Getting fund pricing data ...............................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................Done
indexes = ['wig', 'wig20']

print(f"Getting pricing data for {', '.join(indexes)} ... ", end="")
for index in indexes:
    df_index = pd.read_csv(f'https://stooq.pl/q/d/l/?s={index}&i=y', parse_dates=['Data']) # get raw data as csv
    df_index['name'] = index.upper()
    df_index = df_index.rename(columns={'Data':'date', 'Zamkniecie': 'price'})[['name', 'date', 'price']]
    df = df.append(df_index) # append index data to fund pricing data
print('Done')
Getting pricing data for wig, wig20 ... Done
df['year'] = df.date.apply(lambda d: d.year)

df_fund_year_prices = df.groupby(['name', 'year'])\
   .apply(lambda gr: gr[gr['date'] == gr['date'].max()]['price'])\
   .reset_index()[['name', 'year', 'price']]\
   .drop_duplicates()

# get previous price
df_fund_year_prices['price_prev'] = df_fund_year_prices.price.shift(1)

df_fund_year_prices['price_change_pct'] = (df_fund_year_prices['price'] - df_fund_year_prices['price_prev']) / df_fund_year_prices['price_prev'] * 100
years_on_market_threshold = 20 # limit to funds that are around long enough

df_long_time_on_market = df_fund_year_prices.groupby('name').aggregate({'year':'count'}).rename(columns={'year':'years_on_market'})
df_long_time_on_market = df_long_time_on_market[df_long_time_on_market.years_on_market > years_on_market_threshold]

long_time_on_market_names = df_long_time_on_market.reset_index().name.values

Results

The results look not so bad, but take a look and judge for yourself. The important thing is to takie into consideration compounding and that even small differences in returns early during the investing period produce huge differences for final value of the holdings.

cmap = sns.diverging_palette(h_neg=10, h_pos=134, n=5, center='light', as_cmap=True)

df_fund_year_prices_old = df_fund_year_prices[
    df_fund_year_prices.name.isin(long_time_on_market_names) & 
    (df_fund_year_prices.price_change_pct < 100) & # remove some outliers - fund conversions
    (df_fund_year_prices.price_change_pct > -70) & # remove some outliers
    (df_fund_year_prices.year < datetime.date.today().year) # consider only full years
]

df_fund_year_prices_old.pivot(index='year', columns='name', values='price_change_pct')\
 .style.background_gradient(cmap=cmap, axis=1)\
 .highlight_null('white')
name Esaliens Akcji (d.Legg Mason Akcji) Novo Akcji Pekao Akcji Polskich Santander Akcji Polskich Skarbiec Akcja WIG WIG20
year
1992 nan nan nan nan nan 13.219454 13.201320
1994 nan nan nan nan nan -39.922019 -40.478127
1995 nan nan 2.337398 nan nan 1.509414 8.183060
1996 nan nan 80.834161 nan nan 89.071831 82.068443
1997 nan nan 4.887424 nan nan 2.267340 3.148842
1998 nan nan -14.712042 9.256845 nan -12.765203 -16.541151
1999 nan 29.195117 34.868017 34.486874 36.931818 41.326706 44.102481
2000 -0.547880 -0.192184 0.045517 20.674357 18.153527 -1.305050 1.542547
2001 -16.295409 -18.485237 -16.424022 -11.323529 -20.913082 -21.993994 -33.468415
2002 7.544830 4.527559 1.578661 -14.676617 7.504440 3.192743 -2.706192
2003 29.241685 31.393597 39.067524 50.826045 44.785213 44.919153 33.887925
2004 20.470766 22.151354 19.267823 36.404639 24.762856 27.935024 24.556555
2005 24.898889 23.942505 25.234249 40.481814 28.548562 33.655702 35.417251
2006 54.419411 38.043170 32.327141 49.125757 40.107618 41.602998 23.749600
2007 22.104082 7.770120 6.375512 15.490417 13.457754 10.387846 5.191311
2008 -44.134294 -51.532025 -58.284457 -54.939477 -42.849550 -51.070379 -48.214580
2009 31.035677 45.302961 41.695958 55.199307 33.623770 46.852941 33.468177
2010 18.586439 10.761724 8.744186 12.060302 8.707598 18.766343 14.880354
2011 -17.732453 -21.377876 -42.999715 -31.888391 -23.612468 -20.834889 -21.853238
2012 16.911092 11.501946 15.907954 21.836138 25.573496 26.240283 20.447847
2013 12.903413 -3.336125 6.214933 1.531072 4.732127 8.056495 -7.046125
2014 -3.656471 2.811071 -6.257619 -2.099349 -7.651281 0.257057 -3.541887
2015 -8.919488 -26.588323 -15.908106 -9.392933 -8.187135 -9.624810 -19.723741
2016 0.502953 -0.573980 6.082474 7.600000 10.919017 11.377121 4.774763
2017 32.246145 39.570237 17.055394 18.370508 25.405714 23.171471 26.350671
2018 -12.980441 -13.235902 -14.819427 -13.007066 -16.459044 -9.499704 -7.499563
2019 4.100827 -6.997193 3.167641 -1.263538 -11.333538 0.246800 -5.558215
2020 -9.199757 -31.370316 -25.177137 1.828154 14.510060 -1.395469 -7.725723
plt = df_fund_year_prices_old.pivot(index='year', columns='name', values='price_change_pct')\
                       .plot(figsize=(15,10), ylabel="% difference from last year")

TODO:

  • compounded results and comparison with indexes
  • test if compounded results are simillar with real fund price at the end