Last Update : 19 Oct 2025
Next Update : 29 Oct 2025
DCC - Dynamic Conditional Correlation
GARCH - Generalized Autoregressive Conditional Heteroskedasticity
DCC GARCH is a popular statistical model to analyze financial markets and their correlations. We analyze forex major pair's price with this model but in a particular way, that we are gonna talk about.
There are few pre-requirements for data before fitting into this model.
We implemented full DCC-GARCH, summarize entire workflow — from selecting 3 forex pairs to the final DCC correlation plot.
Below is the step-by-step logical and technical process we’ve been following :
You begin by grouping related currency pairs — for example:
group = ['AUDCAD=X', 'NZDCAD=X', 'AUDUSD=X']
These are chosen manually or algorithmically because they share common currencies (AUD, NZD, CAD), which creates potential correlation and co-movement.
Purpose → find groups that show regional or fundamental linkage (commodity currencies, majors, etc.).
You use Yahoo Finance (yfinance):
import yfinance as yf
data = yf.download(group, start="2010-01-01", end="2025-10-01")['Close']
You end up with a clean dataframe containing closing prices for each of the 3 symbols.
Convert price levels to returns:
returns = np.log(data / data.shift(1)).dropna()
These returns are what DCC and GARCH models use.
For each pair’s returns:
You estimate a GARCH(1,1) model (using arch library).
You extract standardized residuals and conditional variances.
Example:
from arch import arch_model
std_resid = {}
for col in returns.columns:
am = arch_model(returns[col], mean='Zero', vol='GARCH', p=1, q=1)
res = am.fit(disp='off')
std_resid[col] = res.resid / res.conditional_volatility
This gives you whitened residuals — the input to DCC.
You now have a standardized residual matrix:
standardized_residuals = pd.DataFrame(std_resid)
Each column corresponds to one pair (e.g., AUDCAD, NZDCAD, AUDUSD).
mgarch or custom loops).You compute pairwise correlations from ( R_t ):
Example (for 3 pairs):
corrs = pd.DataFrame({
f"{group[0]} vs {group[1]}": Rt[:,0,1],
f"{group[0]} vs {group[2]}": Rt[:,0,2],
f"{group[1]} vs {group[2]}": Rt[:,1,2]
}, index=returns.index)
Then you z-standardize, smooth, and optionally normalize (0–1) for visual comparison:
corrs_z = (corrs - corrs.mean()) / corrs.std()
corrs_smoothed = corrs_z.rolling(10).mean().dropna()
Finally, plot with chosen colors:
colors = ['black', 'blue', 'orange']
corrs_smoothed.plot(color=colors)
plt.title(f"DCC Dynamic Correlations - Group: {group}")
plt.ylabel("Correlation (-1 to 1)")
plt.grid(True)
plt.show()
The lines show time-varying correlation between each pair.
Correlations fluctuating around 0.5–0.9 → strong comovement.
Negative or diverging → decoupling phases (e.g., crisis, different interest rate regimes).
By overlapping all 3, you visually recognize patterns or synchronized movements — perfect for pattern recognition or clustering.
| Step | Description | Output |
|---|---|---|
| 1 | Select 3 related forex pairs | group list |
| 2 | Download historical prices | data |
| 3 | Compute log returns | returns |
| 4 | Fit univariate GARCH(1,1) | standardized residuals |
| 5 | Stack residuals | standardized_residuals DataFrame |
| 6 | Apply DCC recursion | time-varying correlation matrices Rt |
| 7 | Extract pairwise correlations | DataFrame of correlations |
| 8 | Z-score + smooth + plot | dynamic overlapping lines |