On November 7, 2019, USA TODAY published an editorial by Kevin Brady titled New tax cuts would lock in the gains: Rep. Kevin Brady. It can be found on the USA TODAY web site at this link. In the editorial, Brady wrote the following:
As a result, tax revenue is at an all-time high. You heard that right. According to the Treasury Department, total federal receipts are up more than 4% this year. The bottom line: Washington doesn’t have a revenue problem, it has a spending problem.
To investigate the validity of this statement, the following Python code reads the Monthly Treasury Statements for the current issue and previous issues and plots the monthly receipts, oulays, and surplus/deficit starting in October of 2014.
# Monthly Treasury Statement (MTS) - Current Issue:
# https://www.fiscal.treasury.gov/fsreports/rpt/mthTreasStmt/current.htm (before 11/30/2018)
# https://www.fiscal.treasury.gov/reports-statements/mts/current.html (new)
# Monthly Treasury Statement (MTS) - Previous Issues:
# https://www.fiscal.treasury.gov/fsreports/rpt/mthTreasStmt/backissues.htm (before 11/30/2018)
# https://www.fiscal.treasury.gov/reports-statements/mts/previous.html (new)
import pandas as pd
import matplotlib.pyplot as plt
%matplotlib inline
import datetime
import os
from pandas.plotting import register_matplotlib_converters
register_matplotlib_converters()
pd.set_option('display.width', 120)
#pd.set_option('max_rows', 200)
# START OF VARIABLES TO UPDATE
month_index_last = 1 # 1 = October, 4 = January, 7 = April, 10 = July, 12 = September
year_last = 2021 # last fiscal year
yrmo_last = '2010'
moyr_last = '1020'
# variable for debts
cyear_last = 2020 # last calendar year
cmonth_last = 10 # 1 = January
debt_filename_last = 'mtsdebts_1704_2010.csv'
debt_filename_all = 'mtsdebts_1503_2010.csv'
# (Note: manually update two Jupyter URLs and last month in final note below)
# Also, download CPIAUCNS.csv from https://fred.stlouisfed.org/series/CPIAUCNS to update CPI
# END OF VARIABLES TO UPDATE
savefig_recout = "mts" + yrmo_last + "recout12m.png"
savefig_recout15 = "mts" + yrmo_last + "recout12m15.png"
savefig_recoutgdp = "mts" + yrmo_last + "recout12mgdp.png"
savefig_rec15 = "mts" + yrmo_last + "rec12m15.png"
savefig_rec15norm = "mts" + yrmo_last + "rec12m15norm.png"
savefig_recgdpnorm = "mts" + yrmo_last + "rec12mgdpnorm.png"
savefig_recoth15 = "mts" + yrmo_last + "recoth12m15.png"
savefig_def = "mts" + yrmo_last + "def12m.png"
if os.path.isfile(savefig_recout):
os.remove(savefig_recout) # Opt.: os.system("rm "+strFile)
if os.path.isfile(savefig_recout15):
os.remove(savefig_recout15)
if os.path.isfile(savefig_recoutgdp):
os.remove(savefig_recoutgdp)
if os.path.isfile(savefig_rec15):
os.remove(savefig_rec15)
if os.path.isfile(savefig_rec15norm):
os.remove(savefig_rec15norm)
if os.path.isfile(savefig_recgdpnorm):
os.remove(savefig_recgdpnorm)
if os.path.isfile(savefig_recoth15):
os.remove(savefig_recoth15)
if os.path.isfile(savefig_def):
os.remove(savefig_def)
xlabel_last = "Source: Monthly Treasury Statements (see http://econdataus.com/mts" + yrmo_last + ".html)"
# For 1018, had to change extention from .xls to .xlsx
#xlsx_files_last = "https://www.fiscal.treasury.gov/files/reports-statements/mts/mts" + moyr_last + ".xlsx"
# For 0319, had to change extention from .xlsx to .xls - UPDATE IF NEEDED
# For 0419, had to change extention from .xls to .xlsx - UPDATE IF NEEDED
xlsx_files_last = "https://www.fiscal.treasury.gov/files/reports-statements/mts/mts" + moyr_last + ".xlsx"
xlsx_files_yr = [
'https://www.fiscal.treasury.gov/files/reports-statements/mts/mts0915.xls',
'https://www.fiscal.treasury.gov/files/reports-statements/mts/mts0916.xls',
'https://www.fiscal.treasury.gov/files/reports-statements/mts/mts0917.xls',
'https://www.fiscal.treasury.gov/files/reports-statements/mts/mts0918.xls',
'https://www.fiscal.treasury.gov/files/reports-statements/mts/mts0919.xlsx', #UPDATE
'https://www.fiscal.treasury.gov/files/reports-statements/mts/mts0920.xlsx', #UPDATE
xlsx_files_last
]
def joinyear(year):
iyr = year - 2015
#print("BEFORE "+xlsx_files_yr[iyr]) #DEBUG
xx = pd.read_excel(xlsx_files_yr[iyr], sheet_name='Table 7', index_col=0, skiprows=4)
#print(" AFTER "+xlsx_files_yr[iyr]) #DEBUG
if year == 2021 and month_index_last <= 12: # UPDATE for mts1020.xlsx
xx = xx.iloc[[0,1,3,4,5,6,7,8,9,10,97,100], 0:12]
elif year == 2020 and month_index_last == 1: # UPDATE for mts0920.xlsx # was >=
xx = xx.iloc[[0,1,3,4,5,6,7,8,9,10,99,102], 0:12]
elif year == 2020 and month_index_last < 4: # UPDATE for mts1019.xlsx
xx = xx.iloc[[0,1,3,4,5,6,7,8,9,10,97,100], 0:12]
elif year == 2020 and month_index_last == 9: # UPDATE for mts0920.xlsx # was >=
xx = xx.iloc[[0,1,3,4,5,6,7,8,9,10,99,102], 0:12]
elif year == 2020 and month_index_last >= 4: # UPDATE for mts2001.xlsx
xx = xx.iloc[[0,1,3,4,5,6,7,8,9,10,98,101], 0:12]
elif year < 2099: # fix required for mts1118.xls # was 2019
xx = xx.iloc[[0,1,3,4,5,6,7,8,9,10,99,102], 0:12]
else:
#xx = xx.iloc[[0,1,3,4,5,6,7,8,9,10,97,100], 0:12]
xx = xx.iloc[[0,1,3,4,5,6,7,8,9,10,98,101], 0:12] # UPDATE for mts0319.xls
#print(year) #DEBUG
#print(month_index_last) #DEBUG
#print(xx) #DEBUG
years = [year-1,year-1,year-1,year,year,year,year,year,year,year,year,year]
months = ['10','11','12','01','02','03','04','05','06','07','08','09']
#months = [10,11,12,1,2,3,4,5,6,7,8,9]
for i in range(0,12):
years[i] = str(years[i])+"-"+months[i]+"-01"
xx.columns = pd.to_datetime(years)
xx.index = ['Individual','Corporation','Employment','Unemployment','Other Retirement',
'Excise','Estate','Customs','Miscellaneous','Total Receipts','Total Outlays','Surplus/Deficit']
if year >= year_last:
xx = xx.iloc[:, 0:month_index_last]
#print(xx)
return(xx)
def joinyears(start_year, end_year):
yy = joinyear(2015)
for year in range(start_year+1, end_year+1):
yy = yy.join(joinyear(year))
return(yy.T)
def dofilter(ff, numeric=True, rollingsum=False, normalize=False, divisor=1000, adjust=pd.Series([])):
#print(yy)
#print(yy.T)
first = 0
for i in range(0,len(ff.columns)):
#print(ff.iloc[:,i]) #DEBUG
if (numeric):
ff.iloc[:,i] = ff.iloc[:,i].str.replace(',','').astype(int)
if (len(adjust) > 0):
ff.iloc[:,i] = ff.iloc[:,i].mul(adjust)
#zz = zz.mul(deflators, axis='index')
if (rollingsum):
ff.iloc[:,i] = ff.iloc[:,i].rolling(window=12).sum()
first = 11
if (normalize):
ff.iloc[:,i] = ff.iloc[:,i] - ff.iloc[first,i]
ff.iloc[:,i] = ff.iloc[:,i]/divisor
#yy = yy.T
#yy = yy.iloc[:,9:]
return(ff)
yy0 = joinyears(2015, year_last) # year_last gives error
yy = yy0.copy()
zz = dofilter(yy)
#zz = zz.mul(deflators, axis='index')
#print(zz)
# CPIAUCNS.csv was downloaded from https://fred.stlouisfed.org/series/CPIAUCNS
cpiu = pd.read_csv('CPIAUCNS.csv', index_col=0) # UPDATE CPIAUCNS.csv
CPIAUCNS = cpiu.loc['2014-10-01':,'CPIAUCNS']
cpi_deflators = CPIAUCNS[0] / CPIAUCNS
defs = zz.iloc[:,9:].copy()
print('Table 1. U.S. TREASURY RECEIPTS, OUTLAYS, AND DEFICITS: Monthly Amount ($billions)')
print(defs)
fig, ax = plt.subplots(1, 1, figsize=(12, 8))
ax.plot(defs)
ax.set_title('Figure 1. U.S. TREASURY RECEIPTS, OUTLAYS, AND DEFICITS: Monthly Amount')
ax.set_xlabel(xlabel_last)
ax.set_ylabel('Billions of Dollars')
ax.grid(zorder=0)
ytext=-950
ax.axvline(x='2018-1-1', linestyle='dashed', alpha=0.5, color='red')
ax.text(x='2018-1-15', y=ytext, s='Trump Tax Cut', color='red')
ax.axvline(x='2020-3-1', linestyle='dashed', alpha=0.5, color='red')
ax.text(x='2020-3-15', y=ytext, s='COVID-19', color='red')
ax.legend(defs.columns)
The reason for starting in October of 2014 is that this date is the start of fiscal year 2015, the first year for which the Treasury has spreadsheets posted on its site. In any event, the above plot clearly shows that receipts and surpluses peak in April of every year, presumedly due to taxpayers filing their returns. The plot also shows that the prior two months appeared to have had the largest monthly deficits since at least October of 2014. In fact, a ZeroHedge article mentions that the "March budget deficit of \$208.7 billion (in 2018) was 18% higher than $176.2BN deficit recorded last March (in 2017), and was the biggest March budget deficit in US history."
In order to remove the monthly variations that occur each year, it makes sense to look at the 12-month rolling sum of these values. That is done for receipts and outlays in the following Python code and the resulting plot.
yy = yy0.copy()
allones = cpi_deflators.copy()
for i in range(len(allones)):
allones[i] = 1
deflators = allones
zz = dofilter(yy, rollingsum=True, normalize=False, adjust=deflators)
#zz = zz.mul(deflators, axis='index')
dd = zz.iloc[:,9:11].copy()
#print(dd)
dd['% Chng Receipts'] = dd['Total Receipts'].pct_change(periods=12) * 100
dd['% Chng Outlays'] = dd['Total Outlays'].pct_change(periods=12) * 100
dd['Deflators'] = deflators
#dd.loc[:1] = dd[:1] * cpiu[0:3] / cpiu[:3]
#dd.loc[:2] = dd[:2] * cpiu[0:3] / cpiu[:3]
#dd.loc[:3] = dd[:3] * cpiu[0:3] / cpiu[:3]
print('Table 2. U.S. TREASURY RECEIPTS AND OUTLAYS: 12-Month Rolling Sum ($billions)')
print(dd)
dd = dd.iloc[:,0:2]
fig, ax = plt.subplots(1, 1, figsize=(12, 8))
ax.plot(dd)
ax.set_title('Figure 2. U.S. TREASURY RECEIPTS AND OUTLAYS: 12-Month Rolling Sum')
ax.set_xlabel(xlabel_last)
ax.set_ylabel('Billions of Dollars')
ax.grid(zorder=0)
ytext=3030
ax.axvline(x='2018-1-1', linestyle='dashed', alpha=0.5, color='red')
ax.text(x='2018-1-15', y=ytext, s='Trump Tax Cut', color='red')
ax.axvline(x='2020-3-1', linestyle='dashed', alpha=0.5, color='red')
ax.text(x='2020-3-15', y=ytext, s='COVID-19', color='red')
ax.legend(dd.columns)
fig.savefig(savefig_recout)
The fourth column above shows the increase in the 12-month rolling receipts over the prior 12-month period. Hence, the 4.009018 for 2019-09-01 shows that receipts did increase more than 4 percent in fiscal year 2019 as Brady said (though just barely so). However, the 0.417872 for 2018-09-01 shows that receipts increased less than half of one percent in fiscal year 2018. In addition, these numbers are not corrected for inflation. The following code is the same as above except that the receipts and outlays are all corrected for inflation using the CPI-U from the Federal Reserve web site.
yy = yy0.copy()
deflators = cpi_deflators
zz = dofilter(yy, rollingsum=True, normalize=False, adjust=deflators)
print(zz) #DEBUG
#zz = zz.mul(deflators, axis='index')
dd = zz.iloc[:,9:11].copy()
#print(dd)
dd['% Chng Receipts'] = dd['Total Receipts'].pct_change(periods=12) * 100
dd['% Chng Outlays'] = dd['Total Outlays'].pct_change(periods=12) * 100
dd['Deflators'] = deflators
#dd.loc[:1] = dd[:1] * cpiu[0:3] / cpiu[:3]
#dd.loc[:2] = dd[:2] * cpiu[0:3] / cpiu[:3]
#dd.loc[:3] = dd[:3] * cpiu[0:3] / cpiu[:3]
print('Table 3. U.S. TREASURY RECEIPTS AND OUTLAYS: 12-Month Rolling Sum (billions of 2015 $)')
print(dd)
dd = dd.iloc[:,0:2]
fig, ax = plt.subplots(1, 1, figsize=(12, 8))
ax.plot(dd)
ax.set_title('Figure 3. U.S. TREASURY RECEIPTS AND OUTLAYS: 12-Month Rolling Sum (billions of 2015 $)')
ax.set_xlabel(xlabel_last)
ax.set_ylabel('Billions of 2015 Dollars')
ax.grid(zorder=0)
ytext=2750
ax.axvline(x='2018-1-1', linestyle='dashed', alpha=0.5, color='red')
ax.text(x='2018-1-15', y=ytext, s='Trump Tax Cut', color='red')
ax.axvline(x='2020-3-1', linestyle='dashed', alpha=0.5, color='red')
ax.text(x='2020-3-15', y=ytext, s='COVID-19', color='red')
ax.legend(dd.columns)
fig.savefig(savefig_recout15)
As can be seen in the graph above, real receipts generally declined following a local high reached in April of 2018 to the start of 2019. They then started to recover but have not yet reached that high of April of 2018. This is in fact the month of the last year that individual taxes were paid under the old tax rules. The fourth column above shows that real (inflation-corrected) receipts went down over 1.9 percent in fiscal year 2018 and rose just over 2 percent in fiscal year 2019. As a result, they are just about even over two years and down 1.26 percent from the high in April of 2018.
The fifth column shows that real outlays increased just 0.77 percent in fiscal year 2018 but 6.27 percent in fiscal year 2019. Combining those, real outlays have increased 3.09 percent at an annual rate over the last two fiscal years.
However, receipts and outlays have tended to rise with the GDP over the last 60 or more years. The following code is the same as above except that the receipts and outlays are all taken as a percent of GDP as shown at the Bureau of Economic Analysis web site. Since the BEA site shows quarterly GDP numbers, those numbers are applied to the corresponding months in the receipts and outlays numbers. For example, the GDP number for quarter 4 of 2014 is used for October, November, and December of 2014. Also, if the GDP number is not yet available, it is estimated by assuming that the GDP will grow by the same amount as the last recorded quarter.
#import pandas as pd
# pd.read_excel does not work for 2018Q3 and later - save and convert file to CSV
#xx = pd.read_excel('https://www.bea.gov/national/xls/gdplev.xlsx', skiprows=7)
#xx = pd.read_excel('gdplev.xlsx', skiprows=7) #UPDATE - Save GDP file locally
xx = pd.read_excel('gdplev_19q3.xlsx', skiprows=7) #UPDATE - Save GDP file locally
#xx = pd.read_csv('gdplev.csv', skiprows=7) #UPDATE - Save GDP file locally as a CSV file
aa = xx.iloc[0:90, 0:3] #UPDATE (0:90 = 2018, 90 = 98(row in file) - 7(skiprows) - 1(hdr))
aa.columns = ['Year','Current $bil','Chained 2009 $bil']
qq = xx.iloc[0:291, 4:7] #UPDATE (0:291 = 2019Q3, 289 = 297(row in file - 7(skiprows) - 1(hdr))
qq.columns = ['Quarter','Current $bil','Chained 2009 $bil']
# Must convert strings to floats if read from CSV file (comment out values are already floats)
#aa['Chained 2009 $bil'] = aa['Chained 2009 $bil'].str.replace(',', '')
#qq['Chained 2009 $bil'] = qq['Chained 2009 $bil'].str.replace(',', '')
#aa['Chained 2009 $bil'] = aa['Chained 2009 $bil'].astype(float)
#qq['Chained 2009 $bil'] = qq['Chained 2009 $bil'].astype(float)
aa['Pct_Change'] = aa.loc[:,'Chained 2009 $bil'].pct_change()*100
qq['Pct_Change'] = qq.loc[:,'Chained 2009 $bil'].pct_change()*100
#print(qq.head())
#print(qq.tail(23))
qq14q4 = qq.iloc[271:,]
#qq14q4['deflator'] = 100 / qq14q4['Current $bil'] #TEST
#print(qq14q4)
gdp_deflators = cpi_deflators.copy()
gdp_deflator_est = 2*qq14q4.iloc[len(qq14q4)-1, 1] - qq14q4.iloc[len(qq14q4)-2, 1] # ESTIMATE NEXT GDP - UNCOMMENT
gdp_deflators[:] = 100 / gdp_deflator_est # USE NEXT ESTIMATED GDP
#gdp_deflators[:] = 100 / qq14q4.iloc[len(qq14q4)-1, 1] # USE LAST ACTUAL GDP
#gdp_deflators.rename('GDP')
#gdp_deflators.rename(columns = {'CPIAUCNS':'GDP'}, inplace = True)
j = 0
for i in range(len(qq14q4)):
gdp_deflators[j] = 100 / qq14q4.iloc[i,1]
gdp_deflators[j+1] = 100 / qq14q4.iloc[i,1]
gdp_deflators[j+2] = 100 / qq14q4.iloc[i,1]
j = j+3
#print(type(gdp_deflators))
#print(gdp_deflators)
yy = yy0.copy()
deflators = gdp_deflators
zz = dofilter(yy, rollingsum=True, normalize=False, adjust=deflators)
#zz = zz.mul(deflators, axis='index')
dd = zz.iloc[:,9:11].copy()
#print(dd)
dd['% Chng Receipts'] = dd['Total Receipts'].pct_change(periods=12) * 100
dd['% Chng Outlays'] = dd['Total Outlays'].pct_change(periods=12) * 100
dd['Deflators'] = deflators
#dd.loc[:1] = dd[:1] * cpiu[0:3] / cpiu[:3]
#dd.loc[:2] = dd[:2] * cpiu[0:3] / cpiu[:3]
#dd.loc[:3] = dd[:3] * cpiu[0:3] / cpiu[:3]
print('Table 4. U.S. TREASURY RECEIPTS AND OUTLAYS: 12-Month Rolling Sum (percent of GDP)')
print(dd)
dd = dd.iloc[:,0:2]
fig, ax = plt.subplots(1, 1, figsize=(12, 8))
ax.plot(dd)
ax.set_title('Figure 4. U.S. TREASURY RECEIPTS AND OUTLAYS: 12-Month Rolling Sum (percent of GDP) $)')
ax.set_xlabel(xlabel_last)
ax.set_ylabel('Percent of GDP')
ax.grid(zorder=0)
ytext=14
ax.axvline(x='2018-1-1', linestyle='dashed', alpha=0.5, color='red')
ax.text(x='2018-1-15', y=ytext, s='Trump Tax Cut', color='red')
ax.axvline(x='2020-3-1', linestyle='dashed', alpha=0.5, color='red')
ax.text(x='2020-3-15', y=ytext, s='COVID-19', color='red')
ax.legend(dd.columns)
fig.savefig(savefig_recoutgdp)
Surprisingly, outlays appear to have remained fairly steady as a percent of GDP. They have run between 20 and 21 percent of GDP since at least October of 2014 though they did just reach 21.035 percent in October of 2019. During that same period, receipts have dropped from about 18 percent of GDP to just above 16 percent of GDP. Of course, it can be argued that both receipts and outlays need to be restrained to deal with the growing deficit. However, judging strictly be their percent of GDP, receipts have been a bigger contributor to the growing deficit, at least until the past few months.
Focusing on the receipts, the following Python code plots the 12-month rolling sum of the three largest contributors to real receipts. Those are individual income taxes, corporation income taxes, and employment taxes. Employment taxes consist chiefly of payroll taxes.
yy = yy0.copy()
deflators = cpi_deflators
zz = dofilter(yy, rollingsum=True, normalize=False, adjust=deflators)
zz = zz.mul(deflators, axis='index')
dd = zz.iloc[:,0:3].copy()
oo = zz.iloc[:,3:9]
dd['Other'] = oo.sum(axis=1, skipna=False)
dd['Deflators'] = deflators
print('Table 5. SOURCES OF U.S. TREASURY RECEIPTS: 12-Month Rolling Sum (billions of 2015 $)')
print(dd.iloc[11:,:])
fig, ax = plt.subplots(1, 1, figsize=(12, 8))
dd = dd.iloc[:,0:4]
ax.plot(dd)
ax.set_title('Figure 5. SOURCES OF U.S. TREASURY RECEIPTS: 12-Month Rolling Sum (billions of 2015 $)')
ax.set_xlabel(xlabel_last)
ax.set_ylabel('Billions of 2015 Dollars')
ax.grid(zorder=0)
ytext=75
ax.axvline(x='2018-1-1', linestyle='dashed', alpha=0.5, color='red')
ax.text(x='2018-1-15', y=ytext, s='Trump Tax Cut', color='red')
ax.axvline(x='2020-3-1', linestyle='dashed', alpha=0.5, color='red')
ax.text(x='2020-3-15', y=ytext, s='COVID-19', color='red')
ax.legend(dd.columns)
fig.savefig(savefig_rec15)
The fact that individual tax receipts are so much greater than corporate tax receipts makes it difficult to see the detail of the change in each. Following is the same Python code but with normalize set to True. This normalizes all sources of receipts to start at zero.
yy = yy0.copy()
deflators = cpi_deflators
zz = dofilter(yy, rollingsum=True, normalize=True, adjust=deflators)
zz = zz.mul(deflators, axis='index')
dd = zz.iloc[:,0:3].copy()
oo = zz.iloc[:,3:9]
dd['Other'] = oo.sum(axis=1, skipna=False)
dd['Deflators'] = deflators
print('Table 6. SOURCES OF U.S. TREASURY RECEIPTS: Real Change in 12-Month Rolling Sum (billions of 2015 $)')
print(dd.iloc[11:,:])
fig, ax = plt.subplots(1, 1, figsize=(12, 8))
dd = dd.iloc[:,0:4]
ax.plot(dd)
ax.set_title('Figure 6. SOURCES OF U.S. TREASURY RECEIPTS: Real Change in 12-Month Rolling Sum')
ax.set_xlabel(xlabel_last)
ax.set_ylabel('Billions of 2015 Dollars')
ax.grid(zorder=0)
ytext=-245
ax.axvline(x='2018-1-1', linestyle='dashed', alpha=0.5, color='red')
ax.text(x='2018-1-15', y=ytext, s='Trump Tax Cut', color='red')
ax.axvline(x='2020-3-1', linestyle='dashed', alpha=0.5, color='red')
ax.text(x='2020-3-15', y=ytext, s='COVID-19', color='red')
ax.legend(dd.columns)
fig.savefig(savefig_rec15norm)
As can be seen, real yearly individual income tax receipts have dropped a bit since the high reached in April of 2018, under the old tax rules. Real yearly employment tax receipts have risen slightly and real yearly corporation tax receipts have dropped sharply. It is interesting to note that most of the drop in corporation tax receipts have occurred since the passage of the Tax Cuts and Jobs Act of 2017 so that may be the most visible effect of the tax bill on receipts. The surge in individual tax receipts in April of 2018 likely has little to do with the tax bill since the taxes paid in April are based on the prior tax law. Regarding individual tax receipts, the Washington Times article does say the following:
Analysts said they’ll have a better idea of what’s behind the surge as more information rolls in, but for now said it looks like individual taxpayers are paying more because they have higher incomes.
“Those payments were mostly related to economic activity in 2017 and may reflect stronger-than-expected income growth in that year,” the analysts said in their monthly budget review. “Part of the strength in receipts also may reflect larger-than-anticipated payments for economic activity in 2018. The reasons for the added revenues will be better understood as more detailed information becomes available later this year.”
The following Python code likewise normalizes all sources of receipts to start at zero. However, it adjusts the data to the percent of GDP rather than the CPI rate.
yy = yy0.copy()
deflators = gdp_deflators
zz = dofilter(yy, rollingsum=True, normalize=True, adjust=deflators)
zz = zz.mul(deflators, axis='index')
dd = zz.iloc[:,0:3].copy()
oo = zz.iloc[:,3:9]
dd['Other'] = oo.sum(axis=1, skipna=False)
dd['Deflators'] = deflators
print('Table 7. SOURCES OF U.S. TREASURY RECEIPTS: Change in 12-Month Rolling Sum (percent of GDP)')
print(dd.iloc[11:,:])
fig, ax = plt.subplots(1, 1, figsize=(12, 8))
dd = dd.iloc[:,0:4]
ax.plot(dd)
ax.set_title('Figure 7. SOURCES OF U.S. TREASURY RECEIPTS: Change in 12-Month Rolling Sum (percent of GDP)')
ax.set_xlabel(xlabel_last)
ax.set_ylabel('Percent of GDP')
ax.grid(zorder=0)
ytext=-0.0098
ax.axvline(x='2018-1-1', linestyle='dashed', alpha=0.5, color='red')
ax.text(x='2018-1-15', y=ytext, s='Trump Tax Cut', color='red')
ax.axvline(x='2020-3-1', linestyle='dashed', alpha=0.5, color='red')
ax.text(x='2020-3-15', y=ytext, s='COVID-19', color='red')
ax.legend(dd.columns)
fig.savefig(savefig_recgdpnorm)
As can be seen, yearly individual income tax receipts have dropped since the high reached in April of 2018, under the old tax rules, to a level below where they were prior to the tax cut. Yearly employment tax receipts is the only source of receipts that have remained fairly steady as a percent of GDP. However, yearly corporation tax receipts have dropped sharply. In any event, the following Python code shows the real 12-month rolling sum of the other contributors to receipts.
yy = yy0.copy()
deflators = cpi_deflators
zz = dofilter(yy, rollingsum=True, normalize=False, adjust=deflators)
zz = zz.mul(deflators, axis='index')
dd = zz.iloc[:,3:9]
print('Table 8. OTHER SOURCES OF U.S. TREASURY RECEIPTS: 12-Month Rolling Sum (billions of 2015 $)')
print(dd.iloc[11:,:])
fig, ax = plt.subplots(1, 1, figsize=(12, 8))
ax.plot(dd)
ax.set_title('Figure 8. OTHER SOURCES OF U.S. TREASURY RECEIPTS: 12-Month Rolling Sum (billions of 2015 $)')
ax.set_xlabel(xlabel_last)
ax.set_ylabel('Billions of 2015 Dollars')
ax.grid(zorder=0)
ytext=0
ax.axvline(x='2018-1-1', linestyle='dashed', alpha=0.5, color='red')
ax.text(x='2018-1-15', y=ytext, s='Trump Tax Cut', color='red')
ax.axvline(x='2020-3-1', linestyle='dashed', alpha=0.5, color='red')
ax.text(x='2020-3-15', y=ytext, s='COVID-19', color='red')
ax.legend(dd.columns)
fig.savefig(savefig_recoth15)
As can be seen, real yearly receipts from excise taxes and other retirement taxes remained fairly flat. Real yearly receipts from estate taxes remained fairly flat until this year, when they started to decrease. This is likely due to the increase in the exemption under the 2017 tax cut Real yearly receipts from unemployment taxes have decreased slightly but receipts for miscellaneous sources have dropped sharply, being cut about in half since there high in early 2016. The one source of otehr receipts that has risen is real yearly receipts from custom taxes. This is likely due to the increased trade tariffs under Trump.
Due to the incresing gap between receipts and outlays, the deficit is continuing to grow. The following Python code plots the increase the 12-month rolling sum of the deficit.
yy = yy0.copy()
zz = dofilter(yy, rollingsum=True)
#zz = zz.mul(deflators, axis='index')
dd = zz.iloc[:,[11]]
print('Table 9. U.S. TREASURY SURPLUS/DEFICIT(-): 12-Month Rolling Sum ($billions)')
print(dd.iloc[11:,:])
#pd.set_option('display.max_rows', 100)
#print(dd)
fig, ax = plt.subplots(1, 1, figsize=(12, 8))
ax.plot(dd)
ax.set_title('Figure 9. U.S. TREASURY SURPLUS/DEFICIT(-): 12-Month Rolling Sum')
ax.set_xlabel(xlabel_last)
ax.set_ylabel('Billions of Dollars')
ax.grid(zorder=0)
ytext=-3250
ax.axvline(x='2018-1-1', linestyle='dashed', alpha=0.5, color='red')
ax.text(x='2018-1-15', y=ytext, s='Trump Tax Cut', color='red')
ax.axvline(x='2020-3-1', linestyle='dashed', alpha=0.5, color='red')
ax.text(x='2020-3-15', y=ytext, s='COVID-19', color='red')
ax.legend(dd.columns)
fig.savefig(savefig_def)
As can be seen, the annual deficit has increased from just under \$600 billion per year when Trump took office to just over a trillion dollars per year now.
On February 13, 2019, CNSNews.com released a story titled \$1,665,484,000,000: Feds Collect Record Individual Income Taxes in Calendar 2018--as Debt Climbed \\$1,481,349,159,596.80. It states:
At the same time the Treasury was collecting record individual income taxes, the federal debt was climbing from \$20,492,746,546,193.75 at the close of 2017 to \\$21,974,095,705,790.55 at the close of 2018. That was a one-year increase of \$1,481,349,159,596.80.
In order to verify these numbers, the following code takes data from the Monthly Treasury Statements since September of 2014.
import pandas as pd
import matplotlib.pyplot as plt
%matplotlib inline
from pathlib import Path
def joinyear6(month, year):
yr = year % 100
smo = ['00','01','02','03','04','05','06','07','08','09','10','11','12']
filetype = '.xls'
if year <= 2015 and month <= 4:
filetype = '.xlsx'
if year == 2016 and month == 2:
filetype = '.xlsx'
if year == 2018 and month == 7:
filetype = '.xlsx'
if year == 2018 and month >= 10:
filetype = '.xlsx'
if year == 2019 and month <= 2: # UPDATE IF NEEDED
filetype = '.xlsx'
if year == 2019 and month >= 4: # UPDATE IF NEEDED
filetype = '.xlsx'
if year >= 2020: # UPDATE IF NEEDED
filetype = '.xlsx'
filepath = "https://www.fiscal.treasury.gov/files/reports-statements/mts/mts"+smo[month]+str(yr)+filetype
print("BEFORE "+filepath)
if year < 2017:
xx = pd.read_excel(filepath, sheet_name='Table 6', index_col=0, skiprows=6)
elif year == 2017 and month <= 3:
xx = pd.read_excel(filepath, sheet_name='Table 6', index_col=0, skiprows=6)
elif year == 2018 and month == 11:
xx = pd.read_excel(filepath, sheet_name='Table 6', index_col=0, skiprows=6)
elif year == 2019 and month >= 2 and month <= 3: # change >= 2019 to == 2019 (here and in next case)
xx = pd.read_excel(filepath, sheet_name='Table 6', index_col=0, skiprows=6)
elif year == 2019 and month >= 11 and month <= 11: # Debt Held by the Public on line 12 -> skiprows=6
xx = pd.read_excel(filepath, sheet_name='Table 6', index_col=0, skiprows=6)
elif year == 2020 and month >= 1 and month <= 12: # Debt Held by the Public on line 11 -> skiprows=5 DUP
xx = pd.read_excel(filepath, sheet_name='Table 6', index_col=0, skiprows=5)
else: # Debt Held by the Public on line 11 -> skiprows=5 - UPDATE IF NEEDED
xx = pd.read_excel(filepath, sheet_name='Table 6', index_col=0, skiprows=5)
#print(xx) #DEBUG
#print(" AFTER "+filepath) #DEBUG
xx = xx.iloc[4:7, 5]
#print(xx[0]) #DEBUG
xx[0] = int(xx[0].replace(',',''))/1000
xx[1] = int(xx[1].replace(',',''))/1000
xx[2] = int(xx[2].replace(',',''))/1000
xx.index = ['Public','Intragov','Gross']
dd = pd.DataFrame(xx).T
dd.index = [str(year)+"-"+smo[month]+"-01"] # set index to date
#print(dd) #DEBUG
return(dd)
def joinyears6(start_month, start_year, end_month, end_year):
month = start_month
year = start_year
yy = joinyear6(month, year)
month = month + 1
if month > 12:
month = 1
year = year + 1
while year < end_year or (year == end_year and month <= end_month):
xx = joinyear6(month, year)
yy = yy.append(xx)
month = month + 1
if month > 12:
month = 1
year = year + 1
return(yy)
filename = 'mtsdebts_1409_1502.csv'
print("BEFORE "+filename)
zz = pd.read_csv(filename, index_col=0)
filename = 'mtsdebts_1503_1612.csv'
csvfile = Path(filename)
if csvfile.is_file():
print("BEFORE "+filename)
yy = pd.read_csv(filename, index_col=0)
else:
yy = joinyears6(3, 2015, 12, 2016)
yy.to_csv(filename)
zz = zz.append(yy)
filename = 'mtsdebts_1701_1703.csv'
print("BEFORE "+filename)
yy = pd.read_csv(filename, index_col=0)
zz = zz.append(yy)
#filename = 'mtsdebts_1704_1812.csv'
filename = debt_filename_last
csvfile = Path(filename)
if csvfile.is_file():
print("BEFORE "+filename)
yy = pd.read_csv(filename, index_col=0)
else:
#yy = joinyears6(4, 2017, 12, 2018)
yy = joinyears6(4, 2017, cmonth_last, cyear_last)
yy.to_csv(filename)
zz = zz.append(yy)
zz.index = pd.to_datetime(zz.index)
#zz.to_csv('mtsdebts_1503_1812.csv')
zz.to_csv(debt_filename_all)
#zz = pd.read_html(filepath) # for html format
#print(zz)
#print(zz.info())
The following table and graph of the Monthly Treasury Statement numbers show the increase in the national debt.
print('Table 10. U.S. DEBT HELD BY THE PUBLIC AND GROSS DEBT ($billions)')
zz0 = zz
zz0 = zz0.drop('Intragov', 1)
print(zz0)
fig, ax = plt.subplots(1, 1, figsize=(12, 8))
colors = ['C0','C3']
#ax.plot(zz0)
zz0[:].plot(ax = ax, color=colors)
ax.set_title('Figure 10. U.S. DEBT HELD BY THE PUBLIC AND GROSS DEBT')
ax.set_xlabel(xlabel_last)
ax.set_ylabel('Billions of Dollars')
ax.grid(zorder=0)
ytext=12200
ax.axvline(x='2018-1-1', linestyle='dashed', alpha=0.5, color='red')
ax.text(x='2018-1-15', y=ytext, s='Trump Tax Cut', color='red')
ax.axvline(x='2020-3-1', linestyle='dashed', alpha=0.5, color='red')
ax.text(x='2020-3-15', y=ytext, s='COVID-19', color='red')
ax.legend(zz0.columns)
savefig_debts = "mts" + yrmo_last + "debts.png"
if os.path.isfile(savefig_debts):
os.remove(savefig_debts)
fig.savefig(savefig_debts)
As can be seen above, the gross federal debt rose to nearly \$22 trillion at the end of 2018. This is the "federal debt" referenced in the CNSNews.com article. Also shown is the Debt Held by the Public which rose to over \\$16 trillion. The difference between these to measures of the debt is the Intragovernmental Holdings. This is described on Wikipedia as follows:
In the United States, intragovernmental holdings are primarily composed of the Medicare Trust Fund, the Social Security Trust Fund, and Federal Financing Bank securities. A small amount of marketable securities are held by government accounts.
An interesting thing apparent in the graph is how the debt totals went flat for most of 2015 and 2017. This was caused by the debt ceiling, explained by this article as follows:
On March 15, 2015, the nation reached the debt ceiling of \$18.113 trillion. In response, the Treasury Secretary stopped issuing new debt. He took extraordinary measures to keep the debt from exceeding the limit. For example, he stopped payments to federal employee retirement funds. He also sold investments held by those funds. He kept the debt under the limit until Congress passed the Bipartisan Budget Act of 2015 on November 15. The ceiling remained suspended until March 15, 2017. That means the Treasury Department could not allow the statutory debt limit to go one penny higher than the \\$19.808 trillion it was on that day.
Treasury kept the debt under that ceiling until September 8, 2017.
As explained earlier in that article, that was the day that "President Trump signed a bill increasing the debt ceiling to December 8, 2017".
The following graph looks at the 12-month rolling change in these debts, along with the 12-month rolling sum of the deficit shown earlier.
zzdiff = zz.diff()
zzdiff = -zzdiff.iloc[1:,]
zzdiff['Deficit'] = defs.iloc[0:,2]
zzrs = zzdiff.rolling(window=12).sum()
zzrs = zzrs.iloc[11:,]
#print(zz)
#print(zzdiff)
print('Table 11. U.S. DEBTS AND DEFICIT: 12-Month Rolling Change ($billions)')
print(zzrs)
fig, ax = plt.subplots(1, 1, figsize=(12, 8))
colors = ['C0','C2','C3','C1']
#ax.plot(zzrs)
#zzrs[['Public','Intragov','Gross','Deficit']].plot(ax = ax, color=colors)
zzrs[:].plot(ax = ax, color=colors)
ax.set_title('Figure 11. U.S. DEBTS AND DEFICIT: 12-Month Rolling Change')
ax.set_xlabel(xlabel_last)
ax.set_ylabel('Billions of Dollars')
ax.grid(zorder=0)
ytext=-4700
ax.axvline(x='2018-1-1', linestyle='dashed', alpha=0.5, color='red')
ax.text(x='2018-1-15', y=ytext, s='Trump Tax Cut', color='red')
ax.axvline(x='2020-3-1', linestyle='dashed', alpha=0.5, color='red')
ax.text(x='2020-3-15', y=ytext, s='COVID-19', color='red')
ax.legend(zzrs.columns)
savefig_debts12m = "mts" + yrmo_last + "debts12m.png"
if os.path.isfile(savefig_debts12m):
os.remove(savefig_debts12m)
fig.savefig(savefig_debts12m)
As can be seen in the table and graph above, the 12-month changes in the public and gross debts were generally larger (negatively) than the 12-month rolling sum of the deficit. However, the change in the debts reached a minimum in October of 2015 and August of 2017. Non-coincidently, these were the final months of the debt crises after which the debts caught up and continued their prior trends. Since it's been well over a year since the 2017 debt ceiling crisis ended, the latest 12-month rolling changes should not be much affected by it. It's not surprising that the change in the gross federal debt is larger since it includes intergovernmental debt. However, it does seem strange that the change in the Debt Held by the Public is so much larger, at over \$1.287 trillion. This would seem to merit further investigation.
In any event, one likely factor in the increasing debts and deficits is the Tax Cuts and Jobs Act of 2017. The [aforementioned CNSNews.com article] (https://www.cnsnews.com/news/article/terence-p-jeffrey/1665484000000-feds-collect-record-individual-income-taxes-calendar) concluded:
Even as inflation-adjusted individual income taxes increased from calendar year 2017 to calendar year 2018, total federal tax collections declined.
In calendar 2017, total federal tax collections in constant December 2018 dollars were \$3,407,503,740,000. In calendar year 2018, they were \\$3,330,470,000,000—a decline of $77,033,740,000 from 2017.
Corporation income tax collections declined significantly from calendar year 2017 to calendar year 2018. In calendar 2017, the Treasury collected \$290,978,980,000 in corporation income taxes (in constant December 2018 dollars). In calendar 2018, the Treasury collected \\$195,790,000,000 in corporation income taxes—a drop of \$95,188,980,000.
That was a decline in corporation income tax revenue of 32.7 percent.
Note: The Jupyter Notebook from which this post is generated can be found at http://econdataus.com/mts2010.ipynb. It is identical to the one at http://econdataus.com/mts1804.ipynb except that data on the debt was added in December 2018 and it has been updated through October 2020. Links to additional Jupyter Notebooks can be found at http://econdataus.com/jupyter.html. Finally, a copy of this specific page can be found at http://econdataus.com/mts2009.html and a copy of the most recent version of this page can always be found at http://econdataus.com/mts.html .