648 KiB
648 KiB
In [1]:
import MetaTrader5 as mt5
import pandas as pd
import numpy as np
import matplotlib.pyplot as plt
from statsmodels.tsa.stattools import coint
from sklearn.decomposition import PCA
from sklearn.cluster import KMeans
from pykalman import KalmanFilter
import vectorbt as vbt
# -------------------------------
# Step 1: Data Loading using MT5 API
# -------------------------------
if not mt5.initialize():
print("MT5 Initialization Failed")
mt5.shutdown()
quit()
# Retrieve all symbols from MT5
symbols = [s.name for s in mt5.symbols_get()]
print(f"✅ Found {len(symbols)} symbols in MT5")
# Limit to top 50 symbols for performance
symbols = symbols[:50]
# Function to fetch historical data from MT5
def get_mt5_data(symbol, n_bars=1000, timeframe=mt5.TIMEFRAME_D1):
rates = mt5.copy_rates_from_pos(symbol, timeframe, 0, n_bars)
if rates is None:
print(f"⚠️ Could not retrieve data for {symbol}")
return None
df = pd.DataFrame(rates)
df["time"] = pd.to_datetime(df["time"], unit="s")
df.set_index("time", inplace=True)
return df[["close"]]
# Load data for all symbols
data = {symbol: get_mt5_data(symbol) for symbol in symbols if get_mt5_data(symbol) is not None}
# Merge data into one DataFrame with a multi-index (symbol, feature)
df = pd.concat(data.values(), axis=1, keys=data.keys()).dropna()
print(f"📊 Data Loaded for {len(df.columns)//2} Symbols")
✅ Found 2061 symbols in MT5 📊 Data Loaded for 25 Symbols
In [2]:
# -------------------------------
# Step 2: Prepare Price Data for Pair Selection
# -------------------------------
# Extract just the "close" prices to get a DataFrame with single-level columns
df_prices = df.xs('close', axis=1, level=1)
returns = df_prices.pct_change().dropna()
# Compute the correlation matrix (columns are symbols now)
corr_matrix = returns.corr()
print("Correlation Matrix:")
print(corr_matrix)
Correlation Matrix:
EURUSD GBPUSD USDCHF USDJPY USDCAD AUDUSD AUDNZD \
EURUSD 1.000000 0.780128 -0.736896 -0.434969 -0.586613 0.674502 0.020042
GBPUSD 0.780128 1.000000 -0.619409 -0.420554 -0.630336 0.719728 0.021344
USDCHF -0.736896 -0.619409 1.000000 0.540276 0.493095 -0.589198 0.024836
USDJPY -0.434969 -0.420554 0.540276 1.000000 0.252563 -0.407078 0.092968
USDCAD -0.586613 -0.630336 0.493095 0.252563 1.000000 -0.775543 -0.175122
AUDUSD 0.674502 0.719728 -0.589198 -0.407078 -0.775543 1.000000 0.254445
AUDNZD 0.020042 0.021344 0.024836 0.092968 -0.175122 0.254445 1.000000
AUDCAD 0.466675 0.494919 -0.427767 -0.381727 -0.218942 0.785352 0.220496
AUDCHF 0.150693 0.312526 0.190804 -0.003642 -0.495450 0.680458 0.332438
AUDJPY 0.247187 0.302866 -0.073294 0.508348 -0.505924 0.579337 0.322672
CHFJPY 0.164979 0.075318 -0.279135 0.656318 -0.154203 0.064901 0.088684
EURGBP 0.129716 -0.517883 -0.022158 0.075540 0.200760 -0.220507 -0.003304
EURAUD 0.076317 -0.201345 0.069790 0.123846 0.467535 -0.683390 -0.314569
EURJPY 0.349502 0.187286 -0.029188 0.691392 -0.207146 0.116779 0.113334
EURCHF 0.337276 0.201239 0.387249 0.159975 -0.112317 0.098045 0.059334
EURNZD 0.089424 -0.189548 0.084643 0.181434 0.358556 -0.522560 0.315850
EURCAD 0.570032 0.269095 -0.358609 -0.250556 0.330649 -0.001048 -0.153771
GBPCHF 0.157704 0.548169 0.316533 0.067746 -0.236948 0.242437 0.052546
GBPJPY 0.255988 0.461779 -0.012844 0.610297 -0.303377 0.230589 0.110227
CADCHF -0.252025 -0.087914 0.616146 0.347262 -0.379968 0.076600 0.188453
CADJPY -0.038807 0.001811 0.198868 0.788334 -0.395077 0.107089 0.202341
GBPAUD -0.018070 0.182280 0.092229 0.075331 0.340525 -0.549892 -0.325153
GBPCAD 0.432050 0.671358 -0.317319 -0.294898 0.151622 0.175517 -0.140375
GBPNZD -0.004872 0.193578 0.108581 0.133700 0.225985 -0.382261 0.335656
NZDCAD 0.415465 0.439923 -0.409072 -0.406133 -0.087239 0.551517 -0.451996
NZDCHF 0.144437 0.309318 0.180933 -0.055658 -0.409912 0.553028 -0.235439
NZDJPY 0.250581 0.307299 -0.088876 0.489990 -0.451117 0.490462 -0.119616
NZDUSD 0.673139 0.718219 -0.607766 -0.451989 -0.702332 0.891053 -0.208942
USDSGD -0.808615 -0.750526 0.682546 0.563995 0.656198 -0.793887 -0.041271
AUDSGD 0.442617 0.545053 -0.400662 -0.215658 -0.689571 0.919537 0.338295
CHFSGD 0.335283 0.222428 -0.789993 -0.261336 -0.130315 0.147784 -0.055654
EURDKK -0.011267 -0.036107 0.011558 -0.010886 0.002326 -0.030063 0.031869
EURHKD 0.994846 0.772789 -0.729696 -0.431183 -0.581641 0.669474 0.024709
EURNOK -0.151581 -0.308677 0.197846 0.150674 0.519171 -0.517902 -0.086608
EURPLN -0.304999 -0.263237 0.154693 0.122370 0.287150 -0.280998 -0.025852
EURSEK -0.267557 -0.340078 0.262215 0.165725 0.403234 -0.456200 -0.050783
EURSGD 0.807214 0.509212 -0.506677 -0.134883 -0.292870 0.296343 -0.004976
EURTRY 0.333257 0.269465 -0.246226 -0.131904 -0.182316 0.199602 0.006226
EURZAR 0.044968 -0.103782 0.028639 0.086699 0.197120 -0.274857 -0.083140
GBPDKK -0.131499 0.515171 0.030288 -0.066628 -0.200157 0.219327 0.012875
GBPNOK -0.230155 0.020595 0.209628 0.107297 0.379954 -0.367915 -0.078186
GBPSEK -0.315768 0.087188 0.238888 0.090355 0.196097 -0.224007 -0.033351
GBPSGD 0.496274 0.856301 -0.358180 -0.159346 -0.400840 0.423732 0.005438
GBPTRY 0.281417 0.389010 -0.220466 -0.139287 -0.221339 0.241587 0.008949
NOKJPY 0.367305 0.362742 -0.151723 0.415457 -0.513605 0.448873 0.157618
NOKSEK -0.049602 0.074701 0.003827 -0.022183 -0.244101 0.203806 0.070538
SEKJPY 0.427013 0.338836 -0.161708 0.471799 -0.391819 0.346699 0.126414
SGDJPY -0.061900 -0.078301 0.259451 0.881815 -0.070735 -0.034588 0.147259
USDCNH -0.571956 -0.545428 0.498127 0.422045 0.486012 -0.597839 -0.046366
USDCZK -0.881107 -0.697077 0.646868 0.385782 0.558194 -0.620403 -0.000073
AUDCAD AUDCHF AUDJPY ... GBPNOK GBPSEK GBPSGD \
EURUSD 0.466675 0.150693 0.247187 ... -0.230155 -0.315768 0.496274
GBPUSD 0.494919 0.312526 0.302866 ... 0.020595 0.087188 0.856301
USDCHF -0.427767 0.190804 -0.073294 ... 0.209628 0.238888 -0.358180
USDJPY -0.381727 -0.003642 0.508348 ... 0.107297 0.090355 -0.159346
USDCAD -0.218942 -0.495450 -0.505924 ... 0.379954 0.196097 -0.400840
AUDUSD 0.785352 0.680458 0.579337 ... -0.367915 -0.224007 0.423732
AUDNZD 0.220496 0.332438 0.322672 ... -0.078186 -0.033351 0.005438
AUDCAD 1.000000 0.565956 0.399575 ... -0.196221 -0.153524 0.262599
AUDCHF 0.565956 1.000000 0.638453 ... -0.256766 -0.054440 0.190713
AUDJPY 0.399575 0.638453 1.000000 ... -0.250614 -0.129661 0.256832
CHFJPY -0.050720 -0.173207 0.646573 ... -0.063442 -0.110353 0.140370
EURGBP -0.146002 -0.288362 -0.140673 ... -0.341194 -0.565890 -0.675112
EURAUD -0.599313 -0.767567 -0.534300 ... 0.273187 -0.007963 -0.077966
EURJPY -0.023343 0.116142 0.726552 ... -0.073037 -0.158512 0.231417
EURCHF 0.039792 0.469541 0.234600 ... -0.023426 -0.097946 0.178234
EURNZD -0.458246 -0.558219 -0.331283 ... 0.221284 -0.031043 -0.080747
EURCAD 0.321503 -0.327312 -0.225228 ... 0.117067 -0.168921 0.171733
GBPCHF 0.142527 0.581423 0.288795 ... 0.247214 0.362134 0.653186
GBPJPY 0.059248 0.269393 0.761640 ... 0.122838 0.165042 0.591761
CADCHF -0.254767 0.651879 0.382185 ... -0.121187 0.074771 -0.016828
CADJPY -0.221984 0.311628 0.804380 ... -0.138866 -0.039497 0.104590
GBPAUD -0.516244 -0.584795 -0.451655 ... 0.548969 0.422677 0.433145
GBPCAD 0.420465 -0.075036 -0.098236 ... 0.389177 0.299075 0.707910
GBPNZD -0.369797 -0.365745 -0.241540 ... 0.494348 0.398800 0.429507
NZDCAD 0.767643 0.298219 0.157431 ... -0.125556 -0.119188 0.242996
NZDCHF 0.453129 0.835442 0.471793 ... -0.216859 -0.036481 0.196162
NZDJPY 0.316314 0.515959 0.899567 ... -0.226732 -0.120895 0.268549
NZDUSD 0.689204 0.530909 0.436685 ... -0.332387 -0.210989 0.430053
USDSGD -0.583712 -0.344420 -0.244979 ... 0.291769 0.268401 -0.306053
AUDSGD 0.745726 0.754451 0.674381 ... -0.340100 -0.151655 0.407408
CHFSGD 0.100951 -0.535991 -0.093919 ... -0.042204 -0.107670 0.242536
EURDKK -0.044080 -0.025725 -0.037852 ... -0.068231 -0.054789 -0.027115
EURHKD 0.463624 0.150992 0.245835 ... -0.231851 -0.314401 0.492031
EURNOK -0.292664 -0.449901 -0.353495 ... 0.797190 0.214135 -0.178615
EURPLN -0.152179 -0.202186 -0.155986 ... 0.148743 0.215036 -0.138348
EURSEK -0.310669 -0.315561 -0.280924 ... 0.376451 0.706847 -0.193384
EURSGD 0.169826 -0.099198 0.158403 ... -0.082719 -0.246854 0.495780
EURTRY 0.128421 0.019390 0.070045 ... -0.026937 -0.096512 0.198107
EURZAR -0.234060 -0.307788 -0.182175 ... 0.184971 0.014182 -0.000049
GBPDKK 0.144478 0.294371 0.147369 ... 0.343769 0.571244 0.680225
GBPNOK -0.196221 -0.256766 -0.250614 ... 1.000000 0.565720 0.249443
GBPSEK -0.153524 -0.054440 -0.129661 ... 0.565720 1.000000 0.325864
GBPSGD 0.262599 0.190713 0.256832 ... 0.249443 0.325864 1.000000
GBPTRY 0.155539 0.093958 0.103188 ... 0.072453 0.057482 0.366262
NOKJPY 0.189831 0.408101 0.793006 ... -0.581065 -0.254856 0.314942
NOKSEK 0.075289 0.251699 0.172680 ... -0.548945 0.355114 0.060996
SEKJPY 0.151464 0.274446 0.746343 ... -0.259071 -0.507523 0.300513
SGDJPY -0.123542 0.194079 0.754121 ... -0.034170 -0.045399 -0.004955
USDCNH -0.447486 -0.272675 -0.186632 ... 0.204401 0.148480 -0.225541
USDCZK -0.410759 -0.166643 -0.239922 ... 0.233200 0.320419 -0.426494
GBPTRY NOKJPY NOKSEK SEKJPY SGDJPY USDCNH USDCZK
EURUSD 0.281417 0.367305 -0.049602 0.427013 -0.061900 -0.571956 -0.881107
GBPUSD 0.389010 0.362742 0.074701 0.338836 -0.078301 -0.545428 -0.697077
USDCHF -0.220466 -0.151723 0.003827 -0.161708 0.259451 0.498127 0.646868
USDJPY -0.139287 0.415457 -0.022183 0.471799 0.881815 0.422045 0.385782
USDCAD -0.221339 -0.513605 -0.244101 -0.391819 -0.070735 0.486012 0.558194
AUDUSD 0.241587 0.448873 0.203806 0.346699 -0.034588 -0.597839 -0.620403
AUDNZD 0.008949 0.157618 0.070538 0.126414 0.147259 -0.046366 -0.000073
AUDCAD 0.155539 0.189831 0.075289 0.151464 -0.123542 -0.447486 -0.410759
AUDCHF 0.093958 0.408101 0.251699 0.274446 0.194079 -0.272675 -0.166643
AUDJPY 0.103188 0.793006 0.172680 0.746343 0.754121 -0.186632 -0.239922
CHFJPY 0.041248 0.611892 -0.026081 0.685167 0.774575 0.035608 -0.138834
EURGBP -0.226357 -0.071311 -0.185332 0.049402 0.044778 0.084697 -0.097373
EURAUD -0.043852 -0.238907 -0.322484 -0.041345 -0.010056 0.245766 -0.030998
EURJPY 0.080202 0.726216 -0.062158 0.832348 0.867558 -0.018716 -0.304521
EURCHF 0.075029 0.288785 -0.063726 0.356626 0.276797 -0.083697 -0.301266
EURNZD -0.046456 -0.142260 -0.279888 0.035422 0.081427 0.213887 -0.032137
EURCAD 0.102587 -0.093607 -0.305640 0.099188 -0.144576 -0.171067 -0.459961
GBPCHF 0.234189 0.276800 0.096117 0.235910 0.181892 -0.127323 -0.153094
GBPJPY 0.203548 0.722846 0.044254 0.756556 0.793673 -0.063483 -0.230890
CADCHF -0.029673 0.307563 0.226774 0.187223 0.343123 0.090686 0.182686
CADJPY 0.010736 0.722287 0.134918 0.699043 0.882958 0.092465 0.011274
GBPAUD 0.130603 -0.194207 -0.196885 -0.077522 -0.039863 0.194455 0.042925
GBPCAD 0.284675 -0.029624 -0.138230 0.055901 -0.168638 -0.229395 -0.353266
GBPNZD 0.127186 -0.094668 -0.151361 0.000835 0.053467 0.161279 0.042212
NZDCAD 0.141507 0.077171 0.024695 0.063377 -0.204069 -0.375677 -0.376099
NZDCHF 0.094466 0.333169 0.221066 0.213806 0.118090 -0.255230 -0.171058
NZDJPY 0.106488 0.761318 0.151524 0.726707 0.723334 -0.173311 -0.251473
NZDUSD 0.243096 0.384553 0.173964 0.297222 -0.099768 -0.580420 -0.626784
USDSGD -0.254303 -0.282964 -0.061406 -0.258780 0.121736 0.720614 0.741324
AUDSGD 0.183973 0.463450 0.250176 0.334456 0.032393 -0.391870 -0.409643
CHFSGD 0.094198 -0.016495 -0.054639 0.020509 -0.241200 -0.078291 -0.265235
EURDKK -0.010976 0.016808 0.029726 -0.001277 -0.019047 0.005057 0.012764
EURHKD 0.280634 0.365970 -0.047117 0.423811 -0.062986 -0.562073 -0.876379
EURNOK -0.074157 -0.645186 -0.680546 -0.240464 -0.015080 0.261665 0.174212
EURPLN -0.057076 -0.194816 0.034838 -0.238395 -0.029718 0.191912 0.425565
EURSEK -0.130082 -0.371128 0.265947 -0.574695 -0.027070 0.245836 0.298274
EURSGD 0.199391 0.314376 -0.144204 0.436964 0.026660 -0.202339 -0.680463
EURTRY 0.956103 0.116731 -0.045961 0.158437 -0.012373 -0.162475 -0.266109
EURZAR 0.022006 -0.133484 -0.197193 -0.006390 0.007360 0.189717 0.024857
GBPDKK 0.233856 0.081647 0.195001 -0.041527 -0.033322 -0.081112 0.101044
GBPNOK 0.072453 -0.581065 -0.548945 -0.259071 -0.034170 0.204401 0.233200
GBPSEK 0.057482 -0.254856 0.355114 -0.507523 -0.045399 0.148480 0.320419
GBPSGD 0.366262 0.314942 0.060996 0.300513 -0.004955 -0.225541 -0.426494
GBPTRY 1.000000 0.127127 -0.003160 0.140754 -0.018215 -0.170311 -0.224689
NOKJPY 0.127127 1.000000 0.430911 0.798540 0.663458 -0.186918 -0.348383
NOKSEK -0.003160 0.430911 1.000000 -0.188751 0.009826 -0.077549 0.049930
SEKJPY 0.140754 0.798540 -0.188751 1.000000 0.720977 -0.143944 -0.407972
SGDJPY -0.018215 0.663458 0.009826 0.720977 1.000000 0.103027 0.042909
USDCNH -0.170311 -0.186918 -0.077549 -0.143944 0.103027 1.000000 0.519997
USDCZK -0.224689 -0.348383 0.049930 -0.407972 0.042909 0.519997 1.000000
[50 rows x 50 columns]
In [3]:
# -------------------------------
# Step 3: Candidate Pair Selection using Correlation & Cointegration
# -------------------------------
candidate_pairs = []
symbol_list = df_prices.columns.tolist()
for i, sym1 in enumerate(symbol_list):
for sym2 in symbol_list[i+1:]:
corr_value = corr_matrix.loc[sym1, sym2]
if corr_value > 0.8:
# Run cointegration test on the two price series
score, pvalue, _ = coint(df_prices[sym1], df_prices[sym2])
if pvalue < 0.05:
candidate_pairs.append((sym1, sym2, pvalue, corr_value))
print("Candidate Pairs (High Correlation & Cointegrated):")
for pair in candidate_pairs:
print(pair)
if len(candidate_pairs) == 0:
raise ValueError("No candidate pairs found with high correlation and cointegration.")
# Optionally, you can also use PCA & Clustering here to further group symbols,
# but for this example, we'll proceed with candidate pairs.
Candidate Pairs (High Correlation & Cointegrated):
('AUDUSD', 'NZDUSD', np.float64(0.029071952477892585), np.float64(0.891053344243205))
('AUDJPY', 'CADJPY', np.float64(0.020139018449132847), np.float64(0.8043796863766559))
('CHFJPY', 'EURJPY', np.float64(0.025397607487565806), np.float64(0.8154419546327693))
In [4]:
# Optional: PCA & Clustering for additional insight
pca = PCA(n_components=2)
pca_components = pca.fit_transform(returns.T)
pca_df = pd.DataFrame(pca_components, index=returns.columns, columns=['PC1','PC2'])
print("PCA Components:")
print(pca_df)
kmeans = KMeans(n_clusters=2, random_state=42)
pca_df['cluster'] = kmeans.fit_predict(pca_df)
print("Cluster Assignment:")
print(pca_df[['cluster']])
PCA Components:
PC1 PC2
EURUSD -0.068192 -0.020048
GBPUSD -0.089713 -0.018949
USDCHF 0.112433 0.022101
USDJPY 0.072538 0.088778
USDCAD 0.126676 -0.040845
AUDUSD -0.141829 0.039940
AUDNZD 0.023960 0.006956
AUDCAD -0.046538 0.005291
AUDCHF -0.060629 0.068135
AUDJPY -0.100707 0.134864
CHFJPY -0.008567 0.060643
EURGBP 0.053044 -0.007520
EURAUD 0.104557 -0.066161
EURJPY -0.026952 0.074751
EURCHF 0.012880 0.008218
EURNZD 0.097594 -0.052595
EURCAD 0.027150 -0.054857
GBPCHF -0.008660 0.009418
GBPJPY -0.048592 0.075931
CADCHF 0.016843 0.056910
CADJPY -0.022842 0.123524
GBPAUD 0.082859 -0.065064
GBPCAD 0.005416 -0.053869
GBPNZD 0.076163 -0.051713
NZDCAD -0.039870 -0.007613
NZDCHF -0.053501 0.055236
NZDJPY -0.093532 0.121715
NZDUSD -0.134830 0.027091
USDSGD 0.097528 -0.003666
AUDSGD -0.076452 0.042628
CHFSGD 0.016224 -0.031500
EURDKK 0.031571 -0.006063
EURHKD -0.066269 -0.019982
EURNOK 0.125667 -0.069447
EURPLN 0.072275 -0.019012
EURSEK 0.098055 -0.032709
EURSGD -0.002318 -0.017346
EURTRY -0.212486 -0.276113
EURZAR 0.088358 -0.059437
GBPDKK 0.009699 -0.004573
GBPNOK 0.103332 -0.067368
GBPSEK 0.076569 -0.031341
GBPSGD -0.024292 -0.015827
GBPTRY -0.231359 -0.277327
NOKJPY -0.123414 0.136725
NOKSEK 0.002701 0.029036
SEKJPY -0.094317 0.102124
SGDJPY 0.006600 0.086656
USDCNH 0.086062 -0.005762
USDCZK 0.149105 0.000038
Cluster Assignment:
cluster
EURUSD 0
GBPUSD 0
USDCHF 1
USDJPY 1
USDCAD 1
AUDUSD 0
AUDNZD 1
AUDCAD 0
AUDCHF 0
AUDJPY 0
CHFJPY 0
EURGBP 1
EURAUD 1
EURJPY 0
EURCHF 1
EURNZD 1
EURCAD 1
GBPCHF 0
GBPJPY 0
CADCHF 0
CADJPY 0
GBPAUD 1
GBPCAD 1
GBPNZD 1
NZDCAD 0
NZDCHF 0
NZDJPY 0
NZDUSD 0
USDSGD 1
AUDSGD 0
CHFSGD 1
EURDKK 1
EURHKD 0
EURNOK 1
EURPLN 1
EURSEK 1
EURSGD 1
EURTRY 0
EURZAR 1
GBPDKK 1
GBPNOK 1
GBPSEK 1
GBPSGD 0
GBPTRY 0
NOKJPY 0
NOKSEK 0
SEKJPY 0
SGDJPY 0
USDCNH 1
USDCZK 1
c:\Users\moham\miniconda3\envs\ml\Lib\site-packages\joblib\externals\loky\backend\context.py:136: UserWarning: Could not find the number of physical cores for the following reason:
[WinError 2] The system cannot find the file specified
Returning the number of logical cores instead. You can silence this warning by setting LOKY_MAX_CPU_COUNT to the number of cores you want to use.
warnings.warn(
File "c:\Users\moham\miniconda3\envs\ml\Lib\site-packages\joblib\externals\loky\backend\context.py", line 257, in _count_physical_cores
cpu_info = subprocess.run(
^^^^^^^^^^^^^^^
File "c:\Users\moham\miniconda3\envs\ml\Lib\subprocess.py", line 548, in run
with Popen(*popenargs, **kwargs) as process:
^^^^^^^^^^^^^^^^^^^^^^^^^^^
File "c:\Users\moham\miniconda3\envs\ml\Lib\subprocess.py", line 1026, in __init__
self._execute_child(args, executable, preexec_fn, close_fds,
File "c:\Users\moham\miniconda3\envs\ml\Lib\subprocess.py", line 1538, in _execute_child
hp, ht, pid, tid = _winapi.CreateProcess(executable, args,
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
In [5]:
# -------------------------------
# Step 4: Select a Pair & Align the Data
# -------------------------------
# Choose the first candidate pair as an example
pair1, pair2, pval, corr_val = candidate_pairs[0]
print(f"Selected Pair: {pair1} vs {pair2} (p-value: {pval:.4f}, corr: {corr_val:.2f})")
# Align the two series
S1 = df_prices[pair1].dropna()
S2 = df_prices[pair2].dropna()
df_pair = pd.concat([S1, S2], axis=1).dropna()
df_pair.columns = [pair1, pair2]
Selected Pair: AUDUSD vs NZDUSD (p-value: 0.0291, corr: 0.89)
In [6]:
# -------------------------------
# Step 5: Dynamic Signal Generation with Kalman Filter
# -------------------------------
# Function to estimate dynamic hedge ratio using a Kalman Filter
def kalman_filter_estimate(y, x):
n = len(y)
beta = 0.0
P = 1.0
Q = 1e-5 # Process noise variance
R = 1e-2 # Measurement noise variance
beta_history = []
for i in range(n):
beta_pred = beta
P_pred = P + Q
# Kalman Gain computation
K = P_pred * x[i] / (x[i]**2 * P_pred + R)
# Update the state estimate using the measurement y[i]
beta = beta_pred + K * (y[i] - beta_pred * x[i])
P = (1 - K * x[i]) * P_pred
beta_history.append(beta)
return np.array(beta_history)
# Estimate dynamic hedge ratio for the pair: S1 = beta_t * S2 + error
beta_est = kalman_filter_estimate(df_pair[pair1].values, df_pair[pair2].values)
df_pair['beta'] = beta_est
# Compute dynamic spread: difference between S1 and the hedged S2
df_pair['spread'] = df_pair[pair1] - df_pair['beta'] * df_pair[pair2]
# Use a rolling window to compute spread mean and standard deviation (or you could use Kalman state covariances)
window = 60
df_pair['spread_mean'] = df_pair['spread'].rolling(window).mean()
df_pair['spread_std'] = df_pair['spread'].rolling(window).std()
df_pair['zscore'] = (df_pair['spread'] - df_pair['spread_mean']) / df_pair['spread_std']
In [7]:
# -------------------------------
# Step 6: Trading Signal Generation
# -------------------------------
# Define thresholds for signal generation
entry_threshold = 1.5 # for entering trades
exit_threshold = 0.5 # for exiting trades
df_pair['long_signal'] = df_pair['zscore'] < -entry_threshold
df_pair['short_signal'] = df_pair['zscore'] > entry_threshold
df_pair['exit_signal'] = abs(df_pair['zscore']) < exit_threshold
In [8]:
# -------------------------------
# Step 7: Backtesting with vectorbt
# -------------------------------
# Here we simulate trades on the first asset's price (S1) while assuming a hedged position against S2.
entries = df_pair['long_signal']
short_entries = df_pair['short_signal']
exits = df_pair['exit_signal']
portfolio = vbt.Portfolio.from_signals(
close=df_pair[pair1],
entries=entries,
exits=exits,
short_entries=short_entries,
short_exits=exits,
size=1,
size_type='percent',
init_cash=10000,
fees=0.001, # example fee rate
freq='1D'
)
print(portfolio.stats())
portfolio.plot().show()
Start 2021-04-29 00:00:00 End 2025-03-05 00:00:00 Period 996 days 00:00:00 Start Value 10000.0 End Value 10495.182943 Total Return [%] 4.951829 Benchmark Return [%] -19.02309 Max Gross Exposure [%] 100.0 Total Fees Paid 577.999539 Max Drawdown [%] 9.337445 Max Drawdown Duration 521 days 00:00:00 Total Trades 28 Total Closed Trades 28 Total Open Trades 0 Open Trade PnL 0.0 Win Rate [%] 39.285714 Best Trade [%] 6.091998 Worst Trade [%] -4.004551 Avg Winning Trade [%] 2.553578 Avg Losing Trade [%] -1.320344 Avg Winning Trade Duration 16 days 02:10:54.545454545 Avg Losing Trade Duration 16 days 05:38:49.411764705 Profit Factor 1.210917 Expectancy 17.685105 Sharpe Ratio 0.239466 Calmar Ratio 0.191375 Omega Ratio 1.049196 Sortino Ratio 0.354526 dtype: object
[Data output - unsupported data type map[string]interface {} for mime type application/vnd.plotly.v1+json]
In [9]:
# -------------------------------
# Optional: Visualization of the Dynamic Spread and Z-Score
# -------------------------------
plt.figure(figsize=(12, 6))
plt.subplot(2,1,1)
plt.plot(df_pair.index, df_pair['spread'], label='Dynamic Spread')
plt.plot(df_pair.index, df_pair['spread_mean'], label='Rolling Mean', alpha=0.7)
plt.legend()
plt.title('Dynamic Spread via Kalman Filter')
plt.subplot(2,1,2)
plt.plot(df_pair.index, df_pair['zscore'], label='Z-Score', color='orange')
plt.axhline(entry_threshold, color='red', linestyle='--')
plt.axhline(-entry_threshold, color='red', linestyle='--')
plt.axhline(exit_threshold, color='green', linestyle='--')
plt.axhline(-exit_threshold, color='green', linestyle='--')
plt.legend()
plt.title('Z-Score of Spread')
plt.tight_layout()
plt.show()
# -------------------------------
# Shutdown MT5 connection
# -------------------------------
mt5.shutdown()
Out [9]:
True