Created
March 12, 2025 06:33
-
-
Save fasiha/d5ea0bdd9b9f84e02d9566892604cf22 to your computer and use it in GitHub Desktop.
Comparing US stonks ($VTI) and World ex-US ($ACWX) for a couple of months before and after the start of 2025 like the FT article. Code assumes Yahoo! Finance CSV files are available.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
import pandas as pd | |
import numpy as np | |
import pylab as plt | |
from pandas.tseries.offsets import MonthEnd | |
plt.style.use('ggplot') | |
plt.ion() | |
# Load the datasets | |
us_data = pd.read_csv('vti.txt.csv', parse_dates=['Date'], index_col='Date') | |
world_ex_us_data = pd.read_csv('acwx.txt.csv', | |
parse_dates=['Date'], | |
index_col='Date') | |
# Ensure both datasets cover the same date range | |
start_date = max(us_data.index.min(), world_ex_us_data.index.min()) | |
end_date = min(us_data.index.max(), world_ex_us_data.index.max()) | |
us_data = us_data.query("@start_date <= index <= @end_date") | |
world_ex_us_data = world_ex_us_data.query("@start_date <= index <= @end_date") | |
# Calculate daily returns | |
us_data['Daily Return'] = us_data['Close'].pct_change() | |
world_ex_us_data['Daily Return'] = world_ex_us_data['Close'].pct_change() | |
# Combine the daily returns into a single DataFrame | |
returns = pd.DataFrame({ | |
'US': us_data['Daily Return'], | |
'World_ex_US': world_ex_us_data['Daily Return'] | |
}).dropna() | |
# Resample to monthly periods and calculate Pearson correlation for each month | |
def monthly_pearson(df): | |
return df.corr().iloc[0, 1] | |
monthly_corr = returns.groupby([returns.index.year, | |
returns.index.month]).apply(monthly_pearson) | |
# Convert the grouped index to a datetime index | |
monthly_corr.index = [ | |
pd.Timestamp(f'{year}-{month:02d}') + MonthEnd(0) | |
for year, month in monthly_corr.index | |
] | |
# Plot the monthly Pearson correlation coefficients | |
plt.figure(figsize=(14, 8)) | |
plt.plot( | |
monthly_corr.index, | |
monthly_corr.values, | |
label='Monthly Pearson Correlation', | |
) | |
plt.title( | |
'Monthly Pearson Correlation Between US (VTI) and World ex-US (ACWX) Stock Indices' | |
) | |
plt.xlabel('Year') | |
plt.ylabel('Pearson Correlation Coefficient') | |
plt.grid(True) | |
plt.tight_layout() | |
plt.savefig('pearson.png', dpi=300) | |
plt.savefig('pearson.svg') | |
print(monthly_corr.describe()) | |
print(monthly_corr.sort_values()[:10]) | |
fix = lambda ser: ser / ser[-1] * 100 | |
year_months = [(10, 2024), (11, 2024), (12, 2024), (1, 2025), (2, 2025), | |
(3, 2025)] | |
plt.figure(figsize=(14, 8)) | |
for m, y in year_months: | |
idx = np.logical_and(us_data.index.year == y, us_data.index.month == m) | |
plt.plot( | |
us_data.index[idx], | |
fix(us_data['Close'][idx]), | |
label='US', | |
c='r', | |
) | |
plt.plot( | |
world_ex_us_data.index[idx], | |
fix(world_ex_us_data['Close'][idx]), | |
label='World ex-US', | |
c='b', | |
linestyle='-.', | |
) | |
handles, labels = plt.gca().get_legend_handles_labels() | |
plt.legend(handles[:2], labels[:2]) | |
plt.grid(True) | |
plt.xlabel('Date') | |
plt.ylabel('Index') | |
plt.suptitle( | |
'US (\$VTI, red solid) vs World ex-US (\$ACWX, blue dashed), monthly rebased' | |
) | |
plt.tight_layout() | |
plt.savefig('monthly.png', dpi=300) | |
plt.savefig('monthly.svg') | |
fig, axs = plt.subplots(len(year_months), 1, sharex=True, figsize=(14, 8)) | |
for ax, (m, y) in zip(axs, year_months): | |
idx = us_data.index > pd.Timestamp(f"{y}-{m:02}-01") | |
ax.plot( | |
us_data.index[idx], | |
fix(us_data['Close'][idx]), | |
label='US', | |
c='r', | |
) | |
ax.plot(world_ex_us_data.index[idx], | |
fix(world_ex_us_data['Close'][idx]), | |
label='World ex-US', | |
c='b', | |
linestyle='-.') | |
ax.grid(True) | |
ax.set_ylabel('Index') | |
axs[-1].legend() | |
axs[-1].set_xlabel('Date') | |
fig.suptitle( | |
'US (\$VTI, red solid) and World ex-US (\$ACWX, blue dashed), rebased') | |
fig.tight_layout() | |
fig.savefig('monthly-cumul.png', dpi=300) | |
fig.savefig('monthly-cumul.svg') | |
""" | |
Output is: | |
count 204.000000 | |
mean 0.827587 | |
std 0.128098 | |
min 0.011139 | |
25% 0.784704 | |
50% 0.857678 | |
75% 0.918354 | |
max 0.985072 | |
dtype: float64 | |
2024-11-30 0.011139 | |
2014-06-30 0.373610 | |
2024-06-30 0.411560 | |
2020-12-31 0.417950 | |
2008-04-30 0.436142 | |
2017-10-31 0.464513 | |
2025-02-28 0.565186 | |
2014-11-30 0.570015 | |
2024-10-31 0.573248 | |
2019-12-31 0.585244 | |
dtype: float64 | |
""" |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment