Should I be happy to pay rather than receiving money when submitting annual tax returns?¶
Disclaimer: this is not a financial advise of any kind
Here in Spain, one must declare its annual income once a year. By law, a fraction of the salary gets retained to pay those taxes (IRPF). However, it is often adjusted by employers (and by law) so that when declaring taxes the amount of money to be paid/returned is close to zero. When not adjusted properly, one may receive a considerable amount of money back or the other way around (considerable debt in taxes). Latter scenario might feel annoying due to the punctual expenditure but probably it the best considering liquidity.
Here I'll explore whether it is worth having this "debt" and the amount of gains we can generate out of it paying them as late as possible and using a relatively low-risk financial product (ETF)
import pandas as pd
import matplotlib.pyplot as plt
import numpy as np
import yfinance as yf
import mplfinance as mpf
from scipy import stats
import seaborn as sns
# get data from yahoo finance
df = yf.download(
'VWRL.AS', # Vandguard global ETF
start='2012-06-01',
end='2021-06-15',
progress=False,
interval='1mo'
)
# calc "revenue" between months. not sure why close and next open are not equal,
# unless close is from the same day (1st of the month, quite unlikely though)
df['rev'] = df.Open.shift(-1) / df.Open
df.dropna(inplace=True)
df.tail() # why is High and Low data missing from the first rows?
# "succinct" viz. about how it is performing
f, ax = plt.subplots(ncols=2, nrows=2,figsize=(15,15))
# 1st fig, historical price # candles broken due to data integrity
mpf.plot(df, ax=ax[0,0], type='candle', style='yahoo')
# 2nd fig, monthly revenue
df.rev.plot(ax=ax[1,0])
ax[1,0].set_ylabel('fraction revenue')
ax[1,0].axhline(df.rev.mean(), ls='--', c='g',
label=f'mean {round(df.rev.mean(),4)}')
ax[1,0].axhline(1, ls=':', c='gray');
ax[1,0].legend()
# 3rd, viz distribution
df.rev.hist(ax=ax[0,1], bins=40)
ax[0,1].axvline(1, c='k', ls=':')
# 4th, month superposition
(df.groupby(df.index.month)['rev'].agg(mean='mean', sem=stats.sem).
plot(ax=ax[1,1],y='mean', yerr='sem', legend=False, marker='o',
ls='none', xlabel='month', ylabel='profit (monthly)',
capsize=4, color='b'))
ax[1,1].axhline(1, ls=':', c='gray')
ax[1,1].scatter(
df.index.month+np.random.normal(scale=0.1, size=len(df)),
df.rev, color=(0,0,0,0), edgecolor='gray' )
# set titles
ax = ax.flatten()
for i, title in enumerate(
['time candles', 'revenue distribution', 'frac. rev vs time', 'rev. by month']
):
ax[i].set_title(title)
# one last viz
plt.figure(figsize=(8,7))
sns.heatmap(
df.pivot_table(index=df.index.year, columns=df.index.month, values="rev")*100-100,
cmap='coolwarm', center=0, annot=True, fmt='.1f', linewidths=.5,
annot_kws={"fontsize":8}, vmin=-15, vmax=15
).set(xlabel='month', ylabel='year');
Well, obviously there's something wrong with high&low values of the first candles. Apart from that, there's not much to say, an ETF with av. 0.87% monthly returns influenced by global market.
To avoid making extra assumptions we will simply check how much money would have yielded investing some amount of money each month instead of being retained/paid instantaneously. Taxes are paid by the end of June (60%) and early November (remaining 40%) in the next year.
Let's calculate revenue assuming we invested 100€ each month
# simul year 2019, declared on 2020 Jul (1st) & Nov (2nd payment)
ex = df.loc[('2018-12-31'< df.index) & (df.index < '2020-11-01')]
ex.tail()
# get revenue per each month (accu prod/ cum prod)
revs = ex.rev.values[:12][::-1].cumprod()[::-1]
# we flip it to cumprod and then flip again so that december's
# returns are perceived by all of the (100€) investments.
# November's all but 1, etc.
nyear = (revs * 100).sum() # 100€ each month * each revenue
nyear # 1316 rather than 1200, not bad
# now add further interest till Jul (tax campaign ends at the end of June)
money = nyear * ex.rev.values[12:18].prod() # oh shit dang global pandemics
# not adding more money because they fall in next(current) year taxes
# we pay 60% of the taxes (.6*1200)
money -= (1200 * .6)
# remaining interest [Jul to oct, early nov we need to finish tax payment]
money *= ex.rev.values[18:].prod()
money -= (1200 * .4)
# remove from 19 to 21 taxes in those earnings
money * 0.81 # taxes to capital gains (from 19 to 21%)
# not entirely sure it works the same way with losses
26€ does not sound like enough for the amount of headaches
I'll write a function and test other years presenting revenue as percentage of investment
def IRPF_debt_rev(total_money, year, df=df):
"""
total_money: total IRPF debt, assumes it is distributed uniformly
year: year to test
df: dataframe with column called 'rev' with monthly revenue,
indexed with datetime index
"""
ex = df.loc[(f'{year-1}-12-31'< df.index) & (df.index < f'{year+1}-11-01')]
assert len(ex)==22, f'is df complete? filtered df len must be 22'
monthly_debt = total_money / 12
revs = ex.rev.values[:12][::-1].cumprod()[::-1]
money = (revs * monthly_debt).sum()
money *= ex.rev.values[12:18].prod()
money -= (total_money * .6) # 1st payment
money *= ex.rev.values[18:].prod()
money -= (total_money * .4) # 2nd payment
# remove from 19 to 21 taxes in those earnings
return money * 0.81
returns = []
for y in range(2013,2020): # test from 2013 to 2020
returns += [IRPF_debt_rev(1200,y)]
print(f'In {y} it would have returned {round(returns[-1],2)}€'\
f' ({round(100*returns[-1]/1200,2)}%)')
print(f'\non average it represents {round(np.mean(returns),2)}€/year')
print(f'which is {round(100 * np.mean(returns)/1200,2)}% '\
'of the "borrowed" money')
More than 5%, not bad! Worth trying at least.
TBD: explore geometric mean of daily returns