An analysis of global imbalance in the current account.
Published
Mar 14, 2026
Keywords
exorbitant privilege
Summary
Global trade imbalances reveal structural asymmetries in the international financial system, with the United States playing a unique role due to the dominance of the U.S. dollar. While some economies accumulate surpluses, the U.S. consistently runs large trade deficits. This persistent imbalance is not simply a weakness but a reflection of the exorbitant privilege` of the dollar as the world’s primary reserve currency.
Code
# Libraries# =====================================================================import osimport requestsimport pandas as pdimport numpy as npimport seaborn as snsimport matplotlib.pyplot as pltimport matplotlib.patches as mpatchesimport matplotlib.ticker as mtickerfrom matplotlib import font_manager# Data Extraction (Countries)# =====================================================================# Extract JSON and bring data to a dataframeurl ='https://raw.githubusercontent.com/guillemmaya92/world_map/main/Dim_Country.json'response = requests.get(url)data = response.json()df = pd.DataFrame(data)df = pd.DataFrame.from_dict(data, orient='index').reset_index()df_countries = df.rename(columns={'index': 'ISO3'})# Data Extraction (IMF)# =====================================================================#Parameterparameters = ['BCA', 'NGDPD']# Create an empty listrecords = []# Iterar sobre cada parámetrofor parameter in parameters:# Request URL url =f"https://www.imf.org/external/datamapper/api/v1/{parameter}" response = requests.get(url) data = response.json() values = data.get('values', {})# Iterate over each country and yearfor country, years in values.get(parameter, {}).items():for year, value in years.items(): records.append({'Parameter': parameter,'ISO3': country,'Year': int(year),'Value': float(value) })# Create dataframedf_imf = pd.DataFrame(records)# Data Manipulation# =====================================================================# Pivot Parameter to columns and filter nullsdf = df_imf.pivot(index=['ISO3', 'Year'], columns='Parameter', values='Value').reset_index()df = df.dropna(subset=['BCA'], how='any')# Merge queriesdf = df.merge(df_countries, how='left', left_on='ISO3', right_on='ISO3')df = df[['ISO3', 'Country', 'Year', 'BCA', 'NGDPD', 'Analytical', 'Region', 'Cod_Currency']]df = df[df['Region'].notna()]# Custom regionconditions = [ df['ISO3'] =='USA', df['ISO3'] =='GBR', df['ISO3'].isin(['CHN', 'TWN', 'HKG', 'MAC']), df['ISO3'] =='JPN', df['Cod_Currency'] =='EUR', df['BCA'] >=0, df['BCA'] <0]result = ['USA', 'UK', 'Greater China', 'Japan', 'Eurozone', 'Other Surplus', 'Other Deficit']df['Region'] = np.select(conditions, result)# Groupping region and yeardf = df.groupby(["Region", "Year"], as_index=False)[["BCA", "NGDPD"]].sum()# Add total GDPdf['NGDPD'] = df.groupby('Year')['NGDPD'].transform('sum')df['Ratio'] = df['BCA'] / df['NGDPD'] *100# Pivot Regionsdf = df.pivot_table(index="Year", columns="Region", values="Ratio", aggfunc="sum")# Reorder columnsdf = df[["USA", "UK", "Eurozone", "Greater China", "Japan", "Other Surplus", "Other Deficit"]]# Filter perioddf = df.loc[df.index <=2029]# Valuesusa_percent = df.loc[2029, 'USA'] / (df.loc[2029, 'Other Deficit'] + df.loc[2029, 'USA'] + df.loc[2029, 'UK'])uk_percent = df.loc[2029, 'UK'] / (df.loc[2029, 'Other Deficit'] + df.loc[2029, 'USA'] + df.loc[2029, 'UK'])eur_percent = df.loc[2029, 'Eurozone'] / (df.loc[2029, 'Other Surplus'] + df.loc[2029, 'Eurozone'] + df.loc[2029, 'Greater China'] + df.loc[2029, 'Japan'])chn_percent = df.loc[2029, 'Greater China'] / (df.loc[2029, 'Other Surplus'] + df.loc[2029, 'Eurozone'] + df.loc[2029, 'Greater China'] + df.loc[2029, 'Japan'])jpn_percent = df.loc[2029, 'Japan'] / (df.loc[2029, 'Other Surplus'] + df.loc[2029, 'Eurozone'] + df.loc[2029, 'Greater China'] + df.loc[2029, 'Japan'])print(df)# Data Visualization# =====================================================================# Font and styleplt.rcParams.update({'font.family': 'sans-serif', 'font.sans-serif': ['Franklin Gothic'], 'font.size': 9})sns.set(style="white", palette="muted")# Palettepalette = ["#C00000", "#E75527", "#002D64", "#157FFF", "#90bee0", "#E8F1F8", "#FFE1E1"]# Create figurefig, ax = plt.subplots(figsize=(10, 6))# Crear figure and plotax = df.plot(kind="bar", stacked=True, width=0.9, color=palette, legend=False, ax=ax)# Titlefig.add_artist(plt.Line2D([0.11, 0.11], [0.91, 1], linewidth=6, color='#203764', solid_capstyle='butt')) plt.text(0, 1.12, f'Who Absorbs the World’s Surplus?', fontsize=16, fontweight='bold', ha='left', transform=plt.gca().transAxes)plt.text(0, 1.08, f'Global imbalance in the current account', fontsize=11, color='#262626', ha='left', transform=plt.gca().transAxes)plt.text(0, 1.045, f'(as percent of global GDP)', fontsize=8, color='#262626', ha='left', transform=plt.gca().transAxes)# Adjust ticks and gridplt.ylim(-3, 3)ax.set_xticks(range(0, 50, 5)) # Ajustar el rango con len(df)+1ax.set_xticklabels(df.index[::len(df) //10], fontsize=9, rotation=0)ax.yaxis.set_major_formatter(mticker.FuncFormatter(lambda x, pos: f'{int(x):,}'.replace(",", ".")))plt.gca().set_xlabel('')plt.yticks(fontsize=9, color='#282828')plt.grid(axis='x', linestyle='--', color='gray', linewidth=0.5, alpha=0.3)# Custom legend valueshandles = [ mpatches.Patch(color=palette[0], label="USA", linewidth=2), mpatches.Patch(color=palette[1], label="UK", linewidth=2), mpatches.Patch(color=palette[2], label="Eurozone", linewidth=2), mpatches.Patch(color=palette[3], label="Greater China (大中华)", linewidth=2), mpatches.Patch(color=palette[4], label="Japan (日本)", linewidth=2), mpatches.Patch(color=palette[5], label="Other Surplus", linewidth=2), mpatches.Patch(color=palette[6], label="Other Deficit", linewidth=2)]# Legendlegend = plt.legend( handles=handles, loc='lower center', #center bbox_to_anchor=(0.5, -0.12), ncol=8, fontsize=8, frameon=False, handlelength=0.5, handleheight=0.5, borderpad=0.2, columnspacing=0.4)# legend.set_bbox_to_anchor((60, 0), transform=ax.transData)# Change Font (accept chinese characters)prop = font_manager.FontProperties(fname='C:\\Windows\\Fonts\\msyh.ttc')for text in legend.get_texts(): text.set_fontproperties(prop) text.set_fontsize(8)# Add Data Sourceplt.text(0, -0.15, 'Data Source:', transform=plt.gca().transAxes, fontsize=8, fontweight='bold', color='gray')space =" "*23plt.text(0, -0.15, space +'IMF World Economic Outlook Database, 2024', transform=plt.gca().transAxes, fontsize=8, color='gray')# Add textplt.text(50, -0.35, f"← {usa_percent:.0%}", fontsize=7, ha='left', va='bottom')plt.text(50, -0.6, f"← {uk_percent:.0%}", fontsize=7, ha='left', va='bottom')plt.text(50, 0.15, f"← {eur_percent:.0%}", fontsize=7, ha='left', va='bottom')plt.text(50, 0.55, f"← {chn_percent:.0%}", fontsize=7, ha='left', va='bottom')plt.text(50, 0.8, f"← {jpn_percent:.0%}", fontsize=7, ha='left', va='bottom')plt.text(50, 2, f"World\nsurplus", fontsize=7, fontweight ='bold', ha='left', va='top')plt.text(50, -1.8, f"World\ndeficit", fontsize=7, fontweight ='bold', ha='left', va='bottom')# Forecastplt.text(47, 3.1, f'Forecast', fontsize=7, fontweight='bold', color='gray', ha='center')ax.axvspan(44.5, 49.5, color='gray', alpha=0.15, edgecolor='none')# Remove spinesfor spine in plt.gca().spines.values(): spine.set_visible(False)# Save it...download_folder = os.path.join(os.path.expanduser("~"), "Downloads")filename = os.path.join(download_folder, f"FIG_IMF_Global_Surplus.png")plt.savefig(filename, dpi=300, bbox_inches='tight')# Show :)plt.show()