975 KiB
975 KiB
In [ ]:
import MetaTrader5 as mt5
import pandas as pd
import itertools
from statsmodels.tsa.stattools import coint
# Initialize MT5 connection
if not mt5.initialize():
print("MT5 Initialization Failed")
mt5.shutdown()
quit()
# Retrieve all available symbols
symbols = [s.name for s in mt5.symbols_get()]
print(f"✅ Found {len(symbols)} symbols in MT5")
# Limit to top N symbols for testing (remove this for full scan)
symbols = symbols[:50] # Adjust as needed for performance
# Function to fetch price data
def get_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
# Load data for selected symbols
price_data = {symbol: get_data(symbol) for symbol in symbols if get_data(symbol) is not None}
# Find cointegrated pairs
cointegrated_pairs = []
for sym1, sym2 in itertools.combinations(price_data.keys(), 2):
try:
p_value = coint(price_data[sym1]["close"], price_data[sym2]["close"])[1]
if p_value < 0.05: # Cointegration threshold
cointegrated_pairs.append((sym1, sym2, p_value))
print(f"✅ Cointegrated Pair: {sym1} & {sym2} (p={p_value:.4f})")
except Exception as e:
print(f"⚠️ Error processing {sym1} & {sym2}: {e}")
# Sort pairs by strongest cointegration (smallest p-value)
cointegrated_pairs.sort(key=lambda x: x[2])
# Display best pairs
print("\n🔹 Best Cointegrated Pairs:")
for pair in cointegrated_pairs[:10]: # Show top 10 pairs
print(f"⚖️ {pair[0]} & {pair[1]} (p={pair[2]:.4f})")
# Shutdown MT5 connection
mt5.shutdown()
✅ Found 2061 symbols in MT5 ✅ Cointegrated Pair: EURUSD & AUDNZD (p=0.0059) ✅ Cointegrated Pair: USDCHF & EURAUD (p=0.0083) ✅ Cointegrated Pair: USDCHF & EURCAD (p=0.0279) ✅ Cointegrated Pair: USDCHF & GBPAUD (p=0.0099) ✅ Cointegrated Pair: USDCHF & GBPNOK (p=0.0316) ✅ Cointegrated Pair: USDCHF & NOKSEK (p=0.0437) ✅ Cointegrated Pair: USDJPY & AUDJPY (p=0.0037) ✅ Cointegrated Pair: AUDUSD & AUDJPY (p=0.0134) ✅ Cointegrated Pair: AUDUSD & CHFJPY (p=0.0103) ✅ Cointegrated Pair: AUDUSD & EURJPY (p=0.0171) ✅ Cointegrated Pair: AUDUSD & EURCHF (p=0.0032) ✅ Cointegrated Pair: AUDUSD & GBPCHF (p=0.0097) ✅ Cointegrated Pair: AUDUSD & GBPJPY (p=0.0270) ✅ Cointegrated Pair: AUDUSD & CADJPY (p=0.0240) ✅ Cointegrated Pair: AUDUSD & NZDCHF (p=0.0100) ✅ Cointegrated Pair: AUDUSD & NZDJPY (p=0.0260) ✅ Cointegrated Pair: AUDUSD & NZDUSD (p=0.0004) ✅ Cointegrated Pair: AUDUSD & GBPSEK (p=0.0420) ✅ Cointegrated Pair: AUDUSD & SEKJPY (p=0.0312) ✅ Cointegrated Pair: AUDUSD & SGDJPY (p=0.0049) ✅ Cointegrated Pair: AUDUSD & USDCNH (p=0.0080) ✅ Cointegrated Pair: AUDNZD & NZDCAD (p=0.0092) ✅ Cointegrated Pair: AUDNZD & NZDUSD (p=0.0275) ✅ Cointegrated Pair: AUDNZD & EURHKD (p=0.0037) ✅ Cointegrated Pair: AUDNZD & EURSGD (p=0.0096) ✅ Cointegrated Pair: AUDNZD & USDCZK (p=0.0074) ✅ Cointegrated Pair: AUDCAD & AUDJPY (p=0.0259) ✅ Cointegrated Pair: AUDCAD & CHFJPY (p=0.0413) ✅ Cointegrated Pair: AUDCAD & EURJPY (p=0.0406) ✅ Cointegrated Pair: AUDCAD & EURCHF (p=0.0325) ✅ Cointegrated Pair: AUDCAD & GBPCHF (p=0.0235) ✅ Cointegrated Pair: AUDCAD & GBPJPY (p=0.0477) ✅ Cointegrated Pair: AUDCAD & CADJPY (p=0.0228) ✅ Cointegrated Pair: AUDCAD & NZDCAD (p=0.0103) ✅ Cointegrated Pair: AUDCAD & NZDJPY (p=0.0308) ✅ Cointegrated Pair: AUDCAD & CHFSGD (p=0.0473) ✅ Cointegrated Pair: AUDCAD & EURSEK (p=0.0467) ✅ Cointegrated Pair: AUDCAD & EURZAR (p=0.0404) ✅ Cointegrated Pair: AUDCAD & GBPSGD (p=0.0495) ✅ Cointegrated Pair: AUDCAD & NOKJPY (p=0.0103) ✅ Cointegrated Pair: AUDCAD & SEKJPY (p=0.0440) ✅ Cointegrated Pair: AUDCAD & SGDJPY (p=0.0443) ✅ Cointegrated Pair: AUDCAD & USDCNH (p=0.0238) ✅ Cointegrated Pair: AUDCHF & EURJPY (p=0.0447) ✅ Cointegrated Pair: AUDCHF & EURNZD (p=0.0127) ✅ Cointegrated Pair: AUDCHF & EURDKK (p=0.0204) ✅ Cointegrated Pair: AUDCHF & EURNOK (p=0.0409) ✅ Cointegrated Pair: AUDCHF & EURZAR (p=0.0306) ✅ Cointegrated Pair: AUDCHF & GBPSEK (p=0.0232) ✅ Cointegrated Pair: AUDJPY & EURCHF (p=0.0391) ✅ Cointegrated Pair: AUDJPY & CADJPY (p=0.0318) ✅ Cointegrated Pair: AUDJPY & USDCNH (p=0.0480) ✅ Cointegrated Pair: CHFJPY & EURJPY (p=0.0242) ✅ Cointegrated Pair: CHFJPY & EURDKK (p=0.0232) ✅ Cointegrated Pair: CHFJPY & GBPSEK (p=0.0284) ✅ Cointegrated Pair: EURAUD & CHFSGD (p=0.0284) ✅ Cointegrated Pair: EURJPY & NZDCHF (p=0.0353) ✅ Cointegrated Pair: EURJPY & EURDKK (p=0.0029) ✅ Cointegrated Pair: EURJPY & GBPSEK (p=0.0155) ✅ Cointegrated Pair: EURJPY & USDCNH (p=0.0398) ✅ Cointegrated Pair: EURCHF & CADJPY (p=0.0243) ✅ Cointegrated Pair: EURCHF & NZDJPY (p=0.0450) ✅ Cointegrated Pair: EURCHF & NZDUSD (p=0.0040) ✅ Cointegrated Pair: EURCHF & AUDSGD (p=0.0366) ✅ Cointegrated Pair: EURCHF & SGDJPY (p=0.0137) ✅ Cointegrated Pair: EURCHF & USDCNH (p=0.0272) ✅ Cointegrated Pair: EURNZD & GBPJPY (p=0.0498) ✅ Cointegrated Pair: EURNZD & CADCHF (p=0.0067) ✅ Cointegrated Pair: EURNZD & EURDKK (p=0.0234) ✅ Cointegrated Pair: EURNZD & EURNOK (p=0.0157) ✅ Cointegrated Pair: EURNZD & GBPNOK (p=0.0008) ✅ Cointegrated Pair: EURNZD & GBPSEK (p=0.0343) ✅ Cointegrated Pair: EURCAD & NOKSEK (p=0.0463) ✅ Cointegrated Pair: GBPCHF & CADJPY (p=0.0391) ✅ Cointegrated Pair: GBPCHF & NZDUSD (p=0.0135) ✅ Cointegrated Pair: GBPCHF & USDCNH (p=0.0358) ✅ Cointegrated Pair: GBPJPY & EURDKK (p=0.0006) ✅ Cointegrated Pair: GBPJPY & EURTRY (p=0.0008) ✅ Cointegrated Pair: GBPJPY & GBPSEK (p=0.0307) ✅ Cointegrated Pair: GBPJPY & GBPTRY (p=0.0044) ✅ Cointegrated Pair: GBPJPY & SEKJPY (p=0.0242) ✅ Cointegrated Pair: CADCHF & EURDKK (p=0.0390) ✅ Cointegrated Pair: CADCHF & EURNOK (p=0.0225) ✅ Cointegrated Pair: CADCHF & GBPNOK (p=0.0044) ✅ Cointegrated Pair: CADJPY & NZDJPY (p=0.0275) ✅ Cointegrated Pair: GBPAUD & CHFSGD (p=0.0319) ✅ Cointegrated Pair: GBPAUD & EURPLN (p=0.0082) ✅ Cointegrated Pair: GBPCAD & EURPLN (p=0.0464) ✅ Cointegrated Pair: GBPNZD & EURPLN (p=0.0043) ✅ Cointegrated Pair: NZDCAD & NZDJPY (p=0.0267) ✅ Cointegrated Pair: NZDCAD & EURHKD (p=0.0409) ✅ Cointegrated Pair: NZDCAD & NOKJPY (p=0.0145) ✅ Cointegrated Pair: NZDCHF & AUDSGD (p=0.0109) ✅ Cointegrated Pair: NZDCHF & GBPSEK (p=0.0124) ✅ Cointegrated Pair: NZDCHF & USDCNH (p=0.0489) ✅ Cointegrated Pair: NZDJPY & NZDUSD (p=0.0184) ✅ Cointegrated Pair: NZDJPY & EURDKK (p=0.0263) ✅ Cointegrated Pair: NZDJPY & EURSEK (p=0.0455) ✅ Cointegrated Pair: NZDJPY & GBPSEK (p=0.0484) ✅ Cointegrated Pair: NZDJPY & USDCNH (p=0.0337) ✅ Cointegrated Pair: NZDUSD & SGDJPY (p=0.0207) ✅ Cointegrated Pair: USDSGD & EURNOK (p=0.0378) ✅ Cointegrated Pair: USDSGD & NOKSEK (p=0.0244) ✅ Cointegrated Pair: AUDSGD & GBPSEK (p=0.0438) ✅ Cointegrated Pair: CHFSGD & EURDKK (p=0.0184) ✅ Cointegrated Pair: CHFSGD & EURNOK (p=0.0406) ✅ Cointegrated Pair: CHFSGD & EURPLN (p=0.0145) ✅ Cointegrated Pair: CHFSGD & EURZAR (p=0.0343) ✅ Cointegrated Pair: CHFSGD & GBPNOK (p=0.0194) ✅ Cointegrated Pair: CHFSGD & GBPSEK (p=0.0473) ✅ Cointegrated Pair: EURDKK & EURNOK (p=0.0176) ✅ Cointegrated Pair: EURDKK & EURPLN (p=0.0300) ✅ Cointegrated Pair: EURDKK & EURTRY (p=0.0016) ✅ Cointegrated Pair: EURDKK & GBPNOK (p=0.0010) ✅ Cointegrated Pair: EURDKK & GBPSEK (p=0.0109) ✅ Cointegrated Pair: EURDKK & GBPTRY (p=0.0025) ✅ Cointegrated Pair: EURDKK & SEKJPY (p=0.0025) ✅ Cointegrated Pair: EURPLN & GBPNOK (p=0.0148) ✅ Cointegrated Pair: EURSEK & EURZAR (p=0.0334) ✅ Cointegrated Pair: EURTRY & SEKJPY (p=0.0320) ✅ Cointegrated Pair: GBPSEK & GBPTRY (p=0.0201) ✅ Cointegrated Pair: GBPSEK & SEKJPY (p=0.0292) ✅ Cointegrated Pair: GBPSEK & SGDJPY (p=0.0222) ✅ Cointegrated Pair: GBPTRY & SEKJPY (p=0.0315) ✅ Cointegrated Pair: NOKJPY & SGDJPY (p=0.0357) ✅ Cointegrated Pair: NOKJPY & USDCNH (p=0.0328) ✅ Cointegrated Pair: NOKJPY & USDCZK (p=0.0034) ✅ Cointegrated Pair: SEKJPY & SGDJPY (p=0.0484) 🔹 Best Cointegrated Pairs: ⚖️ AUDUSD & NZDUSD (p=0.0004) ⚖️ GBPJPY & EURDKK (p=0.0006) ⚖️ EURNZD & GBPNOK (p=0.0008) ⚖️ GBPJPY & EURTRY (p=0.0008) ⚖️ EURDKK & GBPNOK (p=0.0010) ⚖️ EURDKK & EURTRY (p=0.0016) ⚖️ EURDKK & SEKJPY (p=0.0025) ⚖️ EURDKK & GBPTRY (p=0.0025) ⚖️ EURJPY & EURDKK (p=0.0029) ⚖️ AUDUSD & EURCHF (p=0.0032)
True
In [1]:
import sys
import os
import warnings
from pathlib import Path
# ---------------------------------------------------------------------------
# 1) SET PROJECT ROOT AND UPDATE PATH/WORKING DIRECTORY
# ---------------------------------------------------------------------------
project_root = Path.cwd().parent.parent # Adjust if your notebook is in notebooks/time_series
sys.path.append(str(project_root))
os.chdir(str(project_root))
warnings.filterwarnings("ignore")
import pandas as pd
import numpy as np
import MetaTrader5 as mt5
import vectorbt as vbt
from data.data_loader import get_data_mt5
from features.feature_engineering import add_all_ta_features
import MetaTrader5 as mt5
# Initialize MetaTrader 5 connection
if not mt5.initialize():
print("MT5 Initialization Failed")
mt5.shutdown()
def compute_spread_features(df1, df2):
"""
Compute spread and statistical features for pair trading.
"""
spread = df1["close"] - df2["close"]
z_score = (spread - spread.rolling(window=30).mean()) / spread.rolling(window=30).std()
features = pd.DataFrame({
"spread": spread,
"z_score": z_score,
"rolling_mean": spread.rolling(window=30).mean(),
"rolling_std": spread.rolling(window=30).std(),
})
return features.dropna()
# Example usage
df_AUDUSD = get_data_mt5("AUDUSD", n_bars=1000, timeframe=mt5.TIMEFRAME_D1)
df_NZDUSD = get_data_mt5("EURCHF", n_bars=1000, timeframe=mt5.TIMEFRAME_D1)
spread_features = compute_spread_features(df_AUDUSD, df_NZDUSD)
print(spread_features.tail())
spread z_score rolling_mean rolling_std time 2025-02-27 -0.31186 0.274287 -0.313533 0.006098 2025-02-28 -0.31641 -0.503723 -0.313397 0.005982 2025-03-03 -0.31801 -0.762773 -0.313429 0.006005 2025-03-04 -0.31793 -0.739662 -0.313469 0.006032 2025-03-05 -0.31743 -0.648396 -0.313505 0.006053
In [2]:
from sklearn.ensemble import RandomForestClassifier
from sklearn.model_selection import train_test_split
import numpy as np
# Prepare dataset
spread_features["target"] = np.where(spread_features["z_score"].shift(-1) < 0, 1, 0) # Mean Reversion Signal
X = spread_features.drop("target", axis=1)
y = spread_features["target"]
# Train ML Model
X_train, X_test, y_train, y_test = train_test_split(X, y, test_size=0.2, random_state=42)
model = RandomForestClassifier(n_estimators=100, random_state=42)
model.fit(X_train, y_train)
# Predict signals
spread_features["signal"] = model.predict(X)
print(spread_features.tail())
spread z_score rolling_mean rolling_std target signal time 2025-02-27 -0.31186 0.274287 -0.313533 0.006098 1 0 2025-02-28 -0.31641 -0.503723 -0.313397 0.005982 1 1 2025-03-03 -0.31801 -0.762773 -0.313429 0.006005 1 1 2025-03-04 -0.31793 -0.739662 -0.313469 0.006032 1 1 2025-03-05 -0.31743 -0.648396 -0.313505 0.006053 0 0
In [3]:
import MetaTrader5 as mt5
def execute_trade(symbol1, symbol2, signal):
if signal == 1:
print(f"📈 Enter LONG {symbol1} & SHORT {symbol2}")
mt5.order_send(symbol1, mt5.ORDER_TYPE_BUY, 0.1)
mt5.order_send(symbol2, mt5.ORDER_TYPE_SELL, 0.1)
elif signal == 0:
print(f"📉 Enter SHORT {symbol1} & LONG {symbol2}")
mt5.order_send(symbol1, mt5.ORDER_TYPE_SELL, 0.1)
mt5.order_send(symbol2, mt5.ORDER_TYPE_BUY, 0.1)
# Execute trade for top cointegrated pair
latest_signal = spread_features["signal"].iloc[-1]
execute_trade("AUDUSD", "EURCHF", latest_signal)
📉 Enter SHORT AUDUSD & LONG EURCHF
In [4]:
SL_PIPS = 50
TP_PIPS = 100
def execute_trade(symbol1, symbol2, signal):
price1 = mt5.symbol_info_tick(symbol1).ask
price2 = mt5.symbol_info_tick(symbol2).bid
stop_loss1 = price1 - (SL_PIPS * 0.0001)
take_profit1 = price1 + (TP_PIPS * 0.0001)
stop_loss2 = price2 + (SL_PIPS * 0.0001)
take_profit2 = price2 - (TP_PIPS * 0.0001)
if signal == 1:
mt5.order_send(symbol1, mt5.ORDER_TYPE_BUY, 0.1, price1, sl=stop_loss1, tp=take_profit1)
mt5.order_send(symbol2, mt5.ORDER_TYPE_SELL, 0.1, price2, sl=stop_loss2, tp=take_profit2)
elif signal == 0:
mt5.order_send(symbol1, mt5.ORDER_TYPE_SELL, 0.1, price1, sl=stop_loss1, tp=take_profit1)
mt5.order_send(symbol2, mt5.ORDER_TYPE_BUY, 0.1, price2, sl=stop_loss2, tp=take_profit2)
In [5]:
for time, row in spread_features.iterrows():
signal = row["signal"]
if signal != 0: # Only execute when signal exists
print(f"🔄 {time}: Signal {signal}")
execute_trade("AUDUSD", "NZDUSD", signal)
🔄 2021-06-22 00:00:00: Signal 1.0 🔄 2021-06-23 00:00:00: Signal 1.0 🔄 2021-06-24 00:00:00: Signal 1.0 🔄 2021-06-25 00:00:00: Signal 1.0 🔄 2021-06-28 00:00:00: Signal 1.0 🔄 2021-06-29 00:00:00: Signal 1.0 🔄 2021-06-30 00:00:00: Signal 1.0 🔄 2021-07-01 00:00:00: Signal 1.0 🔄 2021-07-02 00:00:00: Signal 1.0 🔄 2021-07-05 00:00:00: Signal 1.0 🔄 2021-07-06 00:00:00: Signal 1.0 🔄 2021-07-07 00:00:00: Signal 1.0 🔄 2021-07-08 00:00:00: Signal 1.0 🔄 2021-07-09 00:00:00: Signal 1.0 🔄 2021-07-12 00:00:00: Signal 1.0 🔄 2021-07-13 00:00:00: Signal 1.0 🔄 2021-07-14 00:00:00: Signal 1.0 🔄 2021-07-15 00:00:00: Signal 1.0 🔄 2021-07-16 00:00:00: Signal 1.0 🔄 2021-07-19 00:00:00: Signal 1.0 🔄 2021-07-20 00:00:00: Signal 1.0 🔄 2021-07-21 00:00:00: Signal 1.0 🔄 2021-07-22 00:00:00: Signal 1.0 🔄 2021-07-23 00:00:00: Signal 1.0 🔄 2021-07-26 00:00:00: Signal 1.0 🔄 2021-07-27 00:00:00: Signal 1.0 🔄 2021-07-28 00:00:00: Signal 1.0 🔄 2021-07-29 00:00:00: Signal 1.0 🔄 2021-07-30 00:00:00: Signal 1.0 🔄 2021-08-02 00:00:00: Signal 1.0 🔄 2021-08-03 00:00:00: Signal 1.0 🔄 2021-08-04 00:00:00: Signal 1.0 🔄 2021-08-05 00:00:00: Signal 1.0 🔄 2021-08-06 00:00:00: Signal 1.0 🔄 2021-08-09 00:00:00: Signal 1.0 🔄 2021-08-10 00:00:00: Signal 1.0 🔄 2021-08-11 00:00:00: Signal 1.0 🔄 2021-08-12 00:00:00: Signal 1.0 🔄 2021-08-13 00:00:00: Signal 1.0 🔄 2021-08-16 00:00:00: Signal 1.0 🔄 2021-08-17 00:00:00: Signal 1.0 🔄 2021-08-18 00:00:00: Signal 1.0 🔄 2021-08-19 00:00:00: Signal 1.0 🔄 2021-08-20 00:00:00: Signal 1.0 🔄 2021-08-23 00:00:00: Signal 1.0 🔄 2021-08-24 00:00:00: Signal 1.0 🔄 2021-08-25 00:00:00: Signal 1.0 🔄 2021-08-26 00:00:00: Signal 1.0 🔄 2021-08-27 00:00:00: Signal 1.0 🔄 2021-08-30 00:00:00: Signal 1.0 🔄 2021-08-31 00:00:00: Signal 1.0 🔄 2021-09-01 00:00:00: Signal 1.0 🔄 2021-09-02 00:00:00: Signal 1.0 🔄 2021-09-03 00:00:00: Signal 1.0 🔄 2021-09-06 00:00:00: Signal 1.0 🔄 2021-09-07 00:00:00: Signal 1.0 🔄 2021-09-08 00:00:00: Signal 1.0 🔄 2021-09-09 00:00:00: Signal 1.0 🔄 2021-09-10 00:00:00: Signal 1.0 🔄 2021-09-13 00:00:00: Signal 1.0 🔄 2021-09-14 00:00:00: Signal 1.0 🔄 2021-09-15 00:00:00: Signal 1.0 🔄 2021-09-16 00:00:00: Signal 1.0 🔄 2021-09-17 00:00:00: Signal 1.0 🔄 2021-09-20 00:00:00: Signal 1.0 🔄 2021-09-21 00:00:00: Signal 1.0 🔄 2021-09-22 00:00:00: Signal 1.0 🔄 2021-09-23 00:00:00: Signal 1.0 🔄 2021-09-24 00:00:00: Signal 1.0 🔄 2021-09-27 00:00:00: Signal 1.0 🔄 2021-11-01 00:00:00: Signal 1.0 🔄 2021-11-02 00:00:00: Signal 1.0 🔄 2021-11-03 00:00:00: Signal 1.0 🔄 2021-11-04 00:00:00: Signal 1.0 🔄 2021-11-05 00:00:00: Signal 1.0 🔄 2021-11-08 00:00:00: Signal 1.0 🔄 2021-11-09 00:00:00: Signal 1.0 🔄 2021-11-10 00:00:00: Signal 1.0 🔄 2021-11-11 00:00:00: Signal 1.0 🔄 2021-11-12 00:00:00: Signal 1.0 🔄 2021-11-15 00:00:00: Signal 1.0 🔄 2021-11-16 00:00:00: Signal 1.0 🔄 2021-11-17 00:00:00: Signal 1.0 🔄 2021-11-18 00:00:00: Signal 1.0 🔄 2021-11-19 00:00:00: Signal 1.0 🔄 2021-11-22 00:00:00: Signal 1.0 🔄 2021-11-30 00:00:00: Signal 1.0 🔄 2021-12-01 00:00:00: Signal 1.0 🔄 2021-12-02 00:00:00: Signal 1.0 🔄 2022-02-23 00:00:00: Signal 1.0 🔄 2022-03-07 00:00:00: Signal 1.0 🔄 2022-03-08 00:00:00: Signal 1.0 🔄 2022-03-10 00:00:00: Signal 1.0 🔄 2022-03-11 00:00:00: Signal 1.0 🔄 2022-03-14 00:00:00: Signal 1.0 🔄 2022-03-15 00:00:00: Signal 1.0 🔄 2022-04-22 00:00:00: Signal 1.0 🔄 2022-04-25 00:00:00: Signal 1.0 🔄 2022-05-11 00:00:00: Signal 1.0 🔄 2022-05-24 00:00:00: Signal 1.0 🔄 2022-05-25 00:00:00: Signal 1.0 🔄 2022-05-26 00:00:00: Signal 1.0 🔄 2022-06-13 00:00:00: Signal 1.0 🔄 2022-06-16 00:00:00: Signal 1.0 🔄 2022-06-17 00:00:00: Signal 1.0 🔄 2022-06-20 00:00:00: Signal 1.0 🔄 2022-06-21 00:00:00: Signal 1.0 🔄 2022-06-22 00:00:00: Signal 1.0 🔄 2022-06-23 00:00:00: Signal 1.0 🔄 2022-06-24 00:00:00: Signal 1.0 🔄 2022-06-28 00:00:00: Signal 1.0 🔄 2022-06-29 00:00:00: Signal 1.0 🔄 2022-06-30 00:00:00: Signal 1.0 🔄 2022-07-01 00:00:00: Signal 1.0 🔄 2022-07-04 00:00:00: Signal 1.0 🔄 2022-07-05 00:00:00: Signal 1.0 🔄 2022-07-06 00:00:00: Signal 1.0 🔄 2022-07-07 00:00:00: Signal 1.0 🔄 2022-07-08 00:00:00: Signal 1.0 🔄 2022-07-11 00:00:00: Signal 1.0 🔄 2022-07-12 00:00:00: Signal 1.0 🔄 2022-07-13 00:00:00: Signal 1.0 🔄 2022-07-14 00:00:00: Signal 1.0 🔄 2022-08-16 00:00:00: Signal 1.0 🔄 2022-08-17 00:00:00: Signal 1.0 🔄 2022-08-18 00:00:00: Signal 1.0 🔄 2022-09-06 00:00:00: Signal 1.0 🔄 2022-10-05 00:00:00: Signal 1.0 🔄 2022-10-06 00:00:00: Signal 1.0 🔄 2022-10-07 00:00:00: Signal 1.0 🔄 2022-10-10 00:00:00: Signal 1.0 🔄 2022-10-11 00:00:00: Signal 1.0 🔄 2022-10-12 00:00:00: Signal 1.0 🔄 2022-10-13 00:00:00: Signal 1.0 🔄 2022-10-14 00:00:00: Signal 1.0 🔄 2022-10-17 00:00:00: Signal 1.0 🔄 2022-10-18 00:00:00: Signal 1.0 🔄 2022-10-19 00:00:00: Signal 1.0 🔄 2022-10-20 00:00:00: Signal 1.0 🔄 2022-10-21 00:00:00: Signal 1.0 🔄 2022-10-24 00:00:00: Signal 1.0 🔄 2022-10-25 00:00:00: Signal 1.0 🔄 2022-10-26 00:00:00: Signal 1.0 🔄 2022-10-27 00:00:00: Signal 1.0 🔄 2022-10-28 00:00:00: Signal 1.0 🔄 2022-10-31 00:00:00: Signal 1.0 🔄 2022-11-01 00:00:00: Signal 1.0 🔄 2022-11-02 00:00:00: Signal 1.0 🔄 2022-11-03 00:00:00: Signal 1.0 🔄 2022-11-04 00:00:00: Signal 1.0 🔄 2022-11-07 00:00:00: Signal 1.0 🔄 2022-11-08 00:00:00: Signal 1.0 🔄 2022-11-09 00:00:00: Signal 1.0 🔄 2022-11-10 00:00:00: Signal 1.0 🔄 2022-11-11 00:00:00: Signal 1.0 🔄 2022-11-14 00:00:00: Signal 1.0 🔄 2022-11-15 00:00:00: Signal 1.0 🔄 2022-11-16 00:00:00: Signal 1.0 🔄 2022-11-17 00:00:00: Signal 1.0 🔄 2022-11-18 00:00:00: Signal 1.0 🔄 2022-11-21 00:00:00: Signal 1.0 🔄 2022-11-22 00:00:00: Signal 1.0 🔄 2022-11-23 00:00:00: Signal 1.0 🔄 2022-11-24 00:00:00: Signal 1.0 🔄 2022-11-25 00:00:00: Signal 1.0 🔄 2022-11-28 00:00:00: Signal 1.0 🔄 2022-11-29 00:00:00: Signal 1.0 🔄 2022-11-30 00:00:00: Signal 1.0 🔄 2022-12-01 00:00:00: Signal 1.0 🔄 2022-12-02 00:00:00: Signal 1.0 🔄 2022-12-05 00:00:00: Signal 1.0 🔄 2022-12-06 00:00:00: Signal 1.0 🔄 2022-12-07 00:00:00: Signal 1.0 🔄 2022-12-08 00:00:00: Signal 1.0 🔄 2022-12-09 00:00:00: Signal 1.0 🔄 2022-12-12 00:00:00: Signal 1.0 🔄 2022-12-13 00:00:00: Signal 1.0 🔄 2022-12-14 00:00:00: Signal 1.0 🔄 2022-12-15 00:00:00: Signal 1.0 🔄 2022-12-16 00:00:00: Signal 1.0 🔄 2022-12-19 00:00:00: Signal 1.0 🔄 2022-12-20 00:00:00: Signal 1.0 🔄 2022-12-21 00:00:00: Signal 1.0 🔄 2022-12-22 00:00:00: Signal 1.0 🔄 2022-12-27 00:00:00: Signal 1.0 🔄 2022-12-29 00:00:00: Signal 1.0 🔄 2023-02-21 00:00:00: Signal 1.0 🔄 2023-02-22 00:00:00: Signal 1.0 🔄 2023-02-23 00:00:00: Signal 1.0 🔄 2023-02-24 00:00:00: Signal 1.0 🔄 2023-02-27 00:00:00: Signal 1.0 🔄 2023-02-28 00:00:00: Signal 1.0 🔄 2023-03-01 00:00:00: Signal 1.0 🔄 2023-03-02 00:00:00: Signal 1.0 🔄 2023-03-03 00:00:00: Signal 1.0 🔄 2023-03-06 00:00:00: Signal 1.0 🔄 2023-03-07 00:00:00: Signal 1.0 🔄 2023-03-08 00:00:00: Signal 1.0 🔄 2023-03-09 00:00:00: Signal 1.0 🔄 2023-03-10 00:00:00: Signal 1.0 🔄 2023-03-13 00:00:00: Signal 1.0 🔄 2023-03-14 00:00:00: Signal 1.0 🔄 2023-03-15 00:00:00: Signal 1.0 🔄 2023-03-16 00:00:00: Signal 1.0 🔄 2023-03-17 00:00:00: Signal 1.0 🔄 2023-03-20 00:00:00: Signal 1.0 🔄 2023-03-21 00:00:00: Signal 1.0 🔄 2023-03-22 00:00:00: Signal 1.0 🔄 2023-03-23 00:00:00: Signal 1.0 🔄 2023-03-24 00:00:00: Signal 1.0 🔄 2023-03-27 00:00:00: Signal 1.0 🔄 2023-03-28 00:00:00: Signal 1.0 🔄 2023-03-29 00:00:00: Signal 1.0 🔄 2023-03-30 00:00:00: Signal 1.0 🔄 2023-03-31 00:00:00: Signal 1.0 🔄 2023-04-03 00:00:00: Signal 1.0 🔄 2023-04-04 00:00:00: Signal 1.0 🔄 2023-04-05 00:00:00: Signal 1.0 🔄 2023-04-06 00:00:00: Signal 1.0 🔄 2023-04-07 00:00:00: Signal 1.0 🔄 2023-04-28 00:00:00: Signal 1.0 🔄 2023-05-01 00:00:00: Signal 1.0 🔄 2023-05-02 00:00:00: Signal 1.0 🔄 2023-05-03 00:00:00: Signal 1.0 🔄 2023-05-04 00:00:00: Signal 1.0 🔄 2023-05-05 00:00:00: Signal 1.0 🔄 2023-05-08 00:00:00: Signal 1.0 🔄 2023-05-09 00:00:00: Signal 1.0 🔄 2023-05-10 00:00:00: Signal 1.0 🔄 2023-05-11 00:00:00: Signal 1.0 🔄 2023-05-12 00:00:00: Signal 1.0 🔄 2023-05-15 00:00:00: Signal 1.0 🔄 2023-05-16 00:00:00: Signal 1.0 🔄 2023-05-17 00:00:00: Signal 1.0 🔄 2023-05-18 00:00:00: Signal 1.0 🔄 2023-05-19 00:00:00: Signal 1.0 🔄 2023-05-22 00:00:00: Signal 1.0 🔄 2023-05-23 00:00:00: Signal 1.0 🔄 2023-05-24 00:00:00: Signal 1.0 🔄 2023-06-23 00:00:00: Signal 1.0 🔄 2023-06-26 00:00:00: Signal 1.0 🔄 2023-06-27 00:00:00: Signal 1.0 🔄 2023-06-28 00:00:00: Signal 1.0 🔄 2023-06-29 00:00:00: Signal 1.0 🔄 2023-06-30 00:00:00: Signal 1.0 🔄 2023-07-03 00:00:00: Signal 1.0 🔄 2023-07-04 00:00:00: Signal 1.0 🔄 2023-07-05 00:00:00: Signal 1.0 🔄 2023-07-06 00:00:00: Signal 1.0 🔄 2023-07-07 00:00:00: Signal 1.0 🔄 2023-07-10 00:00:00: Signal 1.0 🔄 2023-07-11 00:00:00: Signal 1.0 🔄 2023-07-12 00:00:00: Signal 1.0 🔄 2023-07-13 00:00:00: Signal 1.0 🔄 2023-07-14 00:00:00: Signal 1.0 🔄 2023-07-17 00:00:00: Signal 1.0 🔄 2023-07-18 00:00:00: Signal 1.0 🔄 2023-07-19 00:00:00: Signal 1.0 🔄 2023-07-21 00:00:00: Signal 1.0 🔄 2023-07-24 00:00:00: Signal 1.0 🔄 2023-07-26 00:00:00: Signal 1.0 🔄 2023-07-27 00:00:00: Signal 1.0 🔄 2023-07-28 00:00:00: Signal 1.0 🔄 2023-07-31 00:00:00: Signal 1.0 🔄 2023-08-01 00:00:00: Signal 1.0 🔄 2023-08-02 00:00:00: Signal 1.0 🔄 2023-08-03 00:00:00: Signal 1.0 🔄 2023-08-04 00:00:00: Signal 1.0 🔄 2023-08-07 00:00:00: Signal 1.0 🔄 2023-08-08 00:00:00: Signal 1.0 🔄 2023-08-09 00:00:00: Signal 1.0 🔄 2023-08-16 00:00:00: Signal 1.0 🔄 2023-08-17 00:00:00: Signal 1.0 🔄 2023-08-18 00:00:00: Signal 1.0 🔄 2023-08-21 00:00:00: Signal 1.0 🔄 2023-08-23 00:00:00: Signal 1.0 🔄 2023-08-24 00:00:00: Signal 1.0 🔄 2023-09-07 00:00:00: Signal 1.0 🔄 2023-09-13 00:00:00: Signal 1.0 🔄 2023-09-20 00:00:00: Signal 1.0 🔄 2023-09-21 00:00:00: Signal 1.0 🔄 2023-09-22 00:00:00: Signal 1.0 🔄 2023-09-25 00:00:00: Signal 1.0 🔄 2023-09-26 00:00:00: Signal 1.0 🔄 2023-09-27 00:00:00: Signal 1.0 🔄 2023-09-28 00:00:00: Signal 1.0 🔄 2023-09-29 00:00:00: Signal 1.0 🔄 2023-10-02 00:00:00: Signal 1.0 🔄 2023-10-03 00:00:00: Signal 1.0 🔄 2023-10-04 00:00:00: Signal 1.0 🔄 2023-10-05 00:00:00: Signal 1.0 🔄 2023-10-06 00:00:00: Signal 1.0 🔄 2023-10-09 00:00:00: Signal 1.0 🔄 2023-10-10 00:00:00: Signal 1.0 🔄 2023-10-11 00:00:00: Signal 1.0 🔄 2023-10-12 00:00:00: Signal 1.0 🔄 2023-10-13 00:00:00: Signal 1.0 🔄 2023-11-10 00:00:00: Signal 1.0 🔄 2023-11-23 00:00:00: Signal 1.0 🔄 2023-11-28 00:00:00: Signal 1.0 🔄 2023-11-29 00:00:00: Signal 1.0 🔄 2023-11-30 00:00:00: Signal 1.0 🔄 2023-12-01 00:00:00: Signal 1.0 🔄 2023-12-04 00:00:00: Signal 1.0 🔄 2023-12-05 00:00:00: Signal 1.0 🔄 2023-12-06 00:00:00: Signal 1.0 🔄 2023-12-07 00:00:00: Signal 1.0 🔄 2023-12-08 00:00:00: Signal 1.0 🔄 2023-12-11 00:00:00: Signal 1.0 🔄 2024-01-03 00:00:00: Signal 1.0 🔄 2024-01-04 00:00:00: Signal 1.0 🔄 2024-01-05 00:00:00: Signal 1.0 🔄 2024-01-08 00:00:00: Signal 1.0 🔄 2024-01-09 00:00:00: Signal 1.0 🔄 2024-01-10 00:00:00: Signal 1.0 🔄 2024-01-11 00:00:00: Signal 1.0 🔄 2024-01-12 00:00:00: Signal 1.0 🔄 2024-01-15 00:00:00: Signal 1.0 🔄 2024-01-16 00:00:00: Signal 1.0 🔄 2024-01-17 00:00:00: Signal 1.0 🔄 2024-01-23 00:00:00: Signal 1.0 🔄 2024-01-24 00:00:00: Signal 1.0 🔄 2024-01-26 00:00:00: Signal 1.0 🔄 2024-01-29 00:00:00: Signal 1.0 🔄 2024-01-30 00:00:00: Signal 1.0 🔄 2024-01-31 00:00:00: Signal 1.0 🔄 2024-02-01 00:00:00: Signal 1.0 🔄 2024-02-02 00:00:00: Signal 1.0 🔄 2024-02-05 00:00:00: Signal 1.0 🔄 2024-02-06 00:00:00: Signal 1.0 🔄 2024-02-07 00:00:00: Signal 1.0 🔄 2024-02-08 00:00:00: Signal 1.0 🔄 2024-02-09 00:00:00: Signal 1.0 🔄 2024-02-12 00:00:00: Signal 1.0 🔄 2024-02-13 00:00:00: Signal 1.0 🔄 2024-02-14 00:00:00: Signal 1.0 🔄 2024-02-15 00:00:00: Signal 1.0 🔄 2024-02-16 00:00:00: Signal 1.0 🔄 2024-02-19 00:00:00: Signal 1.0 🔄 2024-02-20 00:00:00: Signal 1.0 🔄 2024-02-21 00:00:00: Signal 1.0 🔄 2024-02-22 00:00:00: Signal 1.0 🔄 2024-02-23 00:00:00: Signal 1.0 🔄 2024-02-26 00:00:00: Signal 1.0 🔄 2024-02-27 00:00:00: Signal 1.0 🔄 2024-02-28 00:00:00: Signal 1.0 🔄 2024-02-29 00:00:00: Signal 1.0 🔄 2024-03-01 00:00:00: Signal 1.0 🔄 2024-04-18 00:00:00: Signal 1.0 🔄 2024-05-15 00:00:00: Signal 1.0 🔄 2024-05-16 00:00:00: Signal 1.0 🔄 2024-05-17 00:00:00: Signal 1.0 🔄 2024-05-21 00:00:00: Signal 1.0 🔄 2024-05-22 00:00:00: Signal 1.0 🔄 2024-05-23 00:00:00: Signal 1.0 🔄 2024-05-24 00:00:00: Signal 1.0 🔄 2024-05-27 00:00:00: Signal 1.0 🔄 2024-05-28 00:00:00: Signal 1.0 🔄 2024-05-29 00:00:00: Signal 1.0 🔄 2024-05-30 00:00:00: Signal 1.0 🔄 2024-05-31 00:00:00: Signal 1.0 🔄 2024-06-03 00:00:00: Signal 1.0 🔄 2024-06-04 00:00:00: Signal 1.0 🔄 2024-06-05 00:00:00: Signal 1.0 🔄 2024-06-06 00:00:00: Signal 1.0 🔄 2024-06-07 00:00:00: Signal 1.0 🔄 2024-06-10 00:00:00: Signal 1.0 🔄 2024-06-11 00:00:00: Signal 1.0 🔄 2024-06-12 00:00:00: Signal 1.0 🔄 2024-06-13 00:00:00: Signal 1.0 🔄 2024-06-14 00:00:00: Signal 1.0 🔄 2024-06-17 00:00:00: Signal 1.0 🔄 2024-06-18 00:00:00: Signal 1.0 🔄 2024-07-30 00:00:00: Signal 1.0 🔄 2024-07-31 00:00:00: Signal 1.0 🔄 2024-08-01 00:00:00: Signal 1.0 🔄 2024-08-02 00:00:00: Signal 1.0 🔄 2024-08-05 00:00:00: Signal 1.0 🔄 2024-08-06 00:00:00: Signal 1.0 🔄 2024-08-07 00:00:00: Signal 1.0 🔄 2024-08-08 00:00:00: Signal 1.0 🔄 2024-08-09 00:00:00: Signal 1.0 🔄 2024-08-12 00:00:00: Signal 1.0 🔄 2024-08-13 00:00:00: Signal 1.0 🔄 2024-08-15 00:00:00: Signal 1.0 🔄 2024-08-16 00:00:00: Signal 1.0 🔄 2024-08-19 00:00:00: Signal 1.0 🔄 2024-08-20 00:00:00: Signal 1.0 🔄 2024-08-21 00:00:00: Signal 1.0 🔄 2024-08-22 00:00:00: Signal 1.0 🔄 2024-08-23 00:00:00: Signal 1.0 🔄 2024-08-26 00:00:00: Signal 1.0 🔄 2024-08-27 00:00:00: Signal 1.0 🔄 2024-08-28 00:00:00: Signal 1.0 🔄 2024-08-29 00:00:00: Signal 1.0 🔄 2024-08-30 00:00:00: Signal 1.0 🔄 2024-09-02 00:00:00: Signal 1.0 🔄 2024-09-03 00:00:00: Signal 1.0 🔄 2024-09-04 00:00:00: Signal 1.0 🔄 2024-09-05 00:00:00: Signal 1.0 🔄 2024-09-06 00:00:00: Signal 1.0 🔄 2024-09-09 00:00:00: Signal 1.0 🔄 2024-09-10 00:00:00: Signal 1.0 🔄 2024-09-11 00:00:00: Signal 1.0 🔄 2024-09-12 00:00:00: Signal 1.0 🔄 2024-09-13 00:00:00: Signal 1.0 🔄 2024-09-16 00:00:00: Signal 1.0 🔄 2024-09-17 00:00:00: Signal 1.0 🔄 2024-09-23 00:00:00: Signal 1.0 🔄 2024-10-28 00:00:00: Signal 1.0 🔄 2024-10-29 00:00:00: Signal 1.0 🔄 2024-10-30 00:00:00: Signal 1.0 🔄 2024-10-31 00:00:00: Signal 1.0 🔄 2024-11-01 00:00:00: Signal 1.0 🔄 2024-11-07 00:00:00: Signal 1.0 🔄 2024-11-08 00:00:00: Signal 1.0 🔄 2024-11-11 00:00:00: Signal 1.0 🔄 2024-11-12 00:00:00: Signal 1.0 🔄 2024-11-13 00:00:00: Signal 1.0 🔄 2024-11-14 00:00:00: Signal 1.0 🔄 2024-11-15 00:00:00: Signal 1.0 🔄 2024-11-18 00:00:00: Signal 1.0 🔄 2024-11-19 00:00:00: Signal 1.0 🔄 2024-11-26 00:00:00: Signal 1.0 🔄 2024-11-27 00:00:00: Signal 1.0 🔄 2024-11-28 00:00:00: Signal 1.0 🔄 2024-11-29 00:00:00: Signal 1.0 🔄 2024-12-02 00:00:00: Signal 1.0 🔄 2024-12-03 00:00:00: Signal 1.0 🔄 2024-12-04 00:00:00: Signal 1.0 🔄 2024-12-05 00:00:00: Signal 1.0 🔄 2024-12-06 00:00:00: Signal 1.0 🔄 2024-12-09 00:00:00: Signal 1.0 🔄 2024-12-10 00:00:00: Signal 1.0 🔄 2024-12-11 00:00:00: Signal 1.0 🔄 2024-12-12 00:00:00: Signal 1.0 🔄 2024-12-13 00:00:00: Signal 1.0 🔄 2024-12-16 00:00:00: Signal 1.0 🔄 2024-12-17 00:00:00: Signal 1.0 🔄 2024-12-19 00:00:00: Signal 1.0 🔄 2024-12-20 00:00:00: Signal 1.0 🔄 2024-12-23 00:00:00: Signal 1.0 🔄 2024-12-24 00:00:00: Signal 1.0 🔄 2024-12-26 00:00:00: Signal 1.0 🔄 2024-12-27 00:00:00: Signal 1.0 🔄 2024-12-30 00:00:00: Signal 1.0 🔄 2025-01-09 00:00:00: Signal 1.0 🔄 2025-01-13 00:00:00: Signal 1.0 🔄 2025-01-20 00:00:00: Signal 1.0 🔄 2025-01-27 00:00:00: Signal 1.0 🔄 2025-01-28 00:00:00: Signal 1.0 🔄 2025-01-29 00:00:00: Signal 1.0 🔄 2025-01-30 00:00:00: Signal 1.0 🔄 2025-02-26 00:00:00: Signal 1.0 🔄 2025-02-27 00:00:00: Signal 1.0 🔄 2025-02-28 00:00:00: Signal 1.0 🔄 2025-03-03 00:00:00: Signal 1.0 🔄 2025-03-04 00:00:00: Signal 1.0
In [1]:
import sys
import os
import warnings
from pathlib import Path
import numpy as np
import pandas as pd
import MetaTrader5 as mt5
import vectorbt as vbt
import statsmodels.api as sm
from statsmodels.tsa.stattools import coint
from sklearn.preprocessing import StandardScaler
import sys
import os
import warnings
from pathlib import Path
# ---------------------------------------------------------------------------
# 1) SET PROJECT ROOT AND UPDATE PATH/WORKING DIRECTORY
# ---------------------------------------------------------------------------
project_root = Path.cwd().parent.parent # Adjust if your notebook is in notebooks/time_series
sys.path.append(str(project_root))
os.chdir(str(project_root))
warnings.filterwarnings("ignore")
# Disable warnings
warnings.filterwarnings("ignore")
# ✅ Load our modules
from data.data_loader import get_data_mt5
# ✅ Initialize MetaTrader 5 connection
if not mt5.initialize():
print("Failed to initialize MT5")
else:
print("✅ Connected to MT5")
# ✅ Select Trading Pair
pair1, pair2 = "AUDUSD", "NZDUSD"
n_bars = 2000
timeframe = mt5.TIMEFRAME_D1 # Use 4-hour data
# ✅ Load historical price data
df1 = get_data_mt5(pair1, n_bars, timeframe)
df2 = get_data_mt5(pair2, n_bars, timeframe)
# ✅ Align timestamps
df = pd.DataFrame({
"time": df1.index,
f"{pair1}": df1["close"],
f"{pair2}": df2["close"]
}).dropna().set_index("time")
print(f"\n✅ Data Loaded ({len(df)} bars)")
print(df.head())
# ✅ Check for Cointegration
coint_p_value = coint(df[pair1], df[pair2])[1]
if coint_p_value > 0.05:
print(f"⚠️ {pair1} & {pair2} are NOT cointegrated (p={coint_p_value:.4f})")
else:
print(f"✅ {pair1} & {pair2} are cointegrated (p={coint_p_value:.4f})")
✅ Connected to MT5
✅ Data Loaded (2000 bars)
AUDUSD NZDUSD
time
2017-06-19 0.75973 0.72291
2017-06-20 0.75798 0.72397
2017-06-21 0.75521 0.72160
2017-06-22 0.75405 0.72642
2017-06-23 0.75691 0.72800
✅ AUDUSD & NZDUSD are cointegrated (p=0.0182)
In [2]:
# ✅ Compute Spread (OLS Regression)
X = sm.add_constant(df[pair2]) # Add intercept
model = sm.OLS(df[pair1], X).fit()
hedge_ratio = model.params[pair2]
df["spread"] = df[pair1] - hedge_ratio * df[pair2]
# ✅ Compute Z-Score for Trading
lookback = 100 # Moving window for Z-score
df["spread_mean"] = df["spread"].rolling(lookback).mean()
df["spread_std"] = df["spread"].rolling(lookback).std()
df["z_score"] = (df["spread"] - df["spread_mean"]) / df["spread_std"]
print("\n✅ Spread & Z-score Calculated")
print(df.tail())
✅ Spread & Z-score Calculated
AUDUSD NZDUSD spread spread_mean spread_std z_score
time
2025-02-27 0.62356 0.56311 0.076056 0.077058 0.002399 -0.417721
2025-02-28 0.62039 0.55958 0.076318 0.076999 0.002342 -0.290614
2025-03-03 0.62247 0.56164 0.076396 0.076947 0.002296 -0.240165
2025-03-04 0.62707 0.56642 0.076348 0.076899 0.002256 -0.243992
2025-03-05 0.62652 0.56594 0.076265 0.076862 0.002236 -0.267003
In [3]:
# ✅ Define Trading Thresholds
entry_threshold = 1.5 # Enter when Z-score > 1.5 or < -1.5
exit_threshold = 0.5 # Exit when Z-score returns to 0.5
# ✅ Generate Trading Signals
df["long_signal"] = df["z_score"] < -entry_threshold
df["short_signal"] = df["z_score"] > entry_threshold
df["exit_signal"] = df["z_score"].abs() < exit_threshold
# ✅ Convert to VectorBT Signals
entries = df["long_signal"].astype(int) - df["short_signal"].astype(int) # 1 for long, -1 for short
exits = df["exit_signal"]
print("\n✅ Trading Signals Generated")
print(df[["spread", "z_score", "long_signal", "short_signal", "exit_signal"]].tail())
✅ Trading Signals Generated
spread z_score long_signal short_signal exit_signal
time
2025-02-27 0.076056 -0.417721 False False True
2025-02-28 0.076318 -0.290614 False False True
2025-03-03 0.076396 -0.240165 False False True
2025-03-04 0.076348 -0.243992 False False True
2025-03-05 0.076265 -0.267003 False False True
In [4]:
# ✅ Define Initial Cash and Trading Fees
init_cash = 10000
fees = 0.0002 # 0.02% transaction cost
# ✅ Run Portfolio Backtest
portfolio = vbt.Portfolio.from_signals(
close=df[pair1],
entries=entries > 0,
exits=exits,
short_entries=entries < 0,
short_exits=exits,
size=1,
size_type="percent",
init_cash=init_cash,
fees=fees,
freq="4H"
)
# ✅ Print Final Results
print("\n✅ Backtest Completed")
print(portfolio.stats())
# ✅ Plot Performance
portfolio.plot().show()
✅ Backtest Completed Start 2017-06-19 00:00:00 End 2025-03-05 00:00:00 Period 333 days 08:00:00 Start Value 10000.0 End Value 12095.674086 Total Return [%] 20.956741 Benchmark Return [%] -17.533861 Max Gross Exposure [%] 100.0 Total Fees Paid 107.953045 Max Drawdown [%] 16.732506 Max Drawdown Duration 118 days 04:00:00 Total Trades 27 Total Closed Trades 27 Total Open Trades 0 Open Trade PnL 0.0 Win Rate [%] 51.851852 Best Trade [%] 9.07005 Worst Trade [%] -9.965733 Avg Winning Trade [%] 3.459788 Avg Losing Trade [%] -2.101339 Avg Winning Trade Duration 4 days 00:17:08.571428571 Avg Losing Trade Duration 7 days 13:50:46.153846153 Profit Factor 1.775186 Expectancy 77.617559 Sharpe Ratio 1.092797 Calmar Ratio 1.384306 Omega Ratio 1.096542 Sortino Ratio 1.630103 dtype: object
[Data output - unsupported data type map[string]interface {} for mime type application/vnd.plotly.v1+json]
In [11]:
# ✅ Extract Performance Metrics
total_return = portfolio.total_return()
sharpe_ratio = portfolio.sharpe_ratio()
win_rate = portfolio.win_rate()
print("\n🔹 **Final Performance Metrics:**")
print(f"🔹 Total Return: {total_return:.2%}")
print(f"🔹 Sharpe Ratio: {sharpe_ratio:.2f}")
print(f"🔹 Win Rate: {win_rate:.2%}")
# ✅ Display Trade Log
print("\n🔹 **Trade Log:**")
print(portfolio.orders.records_readable.tail())
# ✅ Plot Equity Curve
portfolio.plot().show()
[1;31m---------------------------------------------------------------------------[0m [1;31mAttributeError[0m Traceback (most recent call last) Cell [1;32mIn[11], line 4[0m [0;32m 2[0m total_return [38;5;241m=[39m portfolio[38;5;241m.[39mtotal_return() [0;32m 3[0m sharpe_ratio [38;5;241m=[39m portfolio[38;5;241m.[39msharpe_ratio() [1;32m----> 4[0m win_rate [38;5;241m=[39m portfolio[38;5;241m.[39mwin_rate() [0;32m 6[0m [38;5;28mprint[39m([38;5;124m"[39m[38;5;130;01m\n[39;00m[38;5;124m🔹 **Final Performance Metrics:**[39m[38;5;124m"[39m) [0;32m 7[0m [38;5;28mprint[39m([38;5;124mf[39m[38;5;124m"[39m[38;5;124m🔹 Total Return: [39m[38;5;132;01m{[39;00mtotal_return[38;5;132;01m:[39;00m[38;5;124m.2%[39m[38;5;132;01m}[39;00m[38;5;124m"[39m) [1;31mAttributeError[0m: 'Portfolio' object has no attribute 'win_rate'
In [ ]:
import MetaTrader5 as mt5
def execute_trade(pair1, pair2, signal, lot_size=0.1):
"""
Execute trades in MT5 for a pair trading strategy.
- pair1: First currency pair (e.g., "AUDUSD")
- pair2: Second currency pair (e.g., "NZDUSD")
- signal: 1 for LONG pair1 & SHORT pair2, -1 for SHORT pair1 & LONG pair2, 0 to close
- lot_size: Position size in lots
"""
if not mt5.initialize():
print("MT5 Initialization Failed")
return
# Define order types
if signal == 1:
print(f"📈 Entering LONG {pair1} & SHORT {pair2}")
order1 = mt5.ORDER_TYPE_BUY
order2 = mt5.ORDER_TYPE_SELL
elif signal == -1:
print(f"📉 Entering SHORT {pair1} & LONG {pair2}")
order1 = mt5.ORDER_TYPE_SELL
order2 = mt5.ORDER_TYPE_BUY
else:
print(f"🔄 Closing all positions for {pair1} & {pair2}")
close_all_positions(pair1, pair2)
return
# Place trades
request1 = {
"action": mt5.TRADE_ACTION_DEAL,
"symbol": pair1,
"volume": lot_size,
"type": order1,
"price": mt5.symbol_info_tick(pair1).ask if order1 == mt5.ORDER_TYPE_BUY else mt5.symbol_info_tick(pair1).bid,
"deviation": 10,
"magic": 123456,
"comment": "Pair Trading",
"type_time": mt5.ORDER_TIME_GTC,
"type_filling": mt5.ORDER_FILLING_IOC,
}
request2 = {
"action": mt5.TRADE_ACTION_DEAL,
"symbol": pair2,
"volume": lot_size,
"type": order2,
"price": mt5.symbol_info_tick(pair2).ask if order2 == mt5.ORDER_TYPE_BUY else mt5.symbol_info_tick(pair2).bid,
"deviation": 10,
"magic": 123456,
"comment": "Pair Trading",
"type_time": mt5.ORDER_TIME_GTC,
"type_filling": mt5.ORDER_FILLING_IOC,
}
# Send orders
result1 = mt5.order_send(request1)
result2 = mt5.order_send(request2)
if result1.retcode == mt5.TRADE_RETCODE_DONE and result2.retcode == mt5.TRADE_RETCODE_DONE:
print(f"✅ Trade executed: {pair1} {order1}, {pair2} {order2}")
else:
print(f"❌ Trade failed: {result1.comment}, {result2.comment}")
mt5.shutdown()
def close_all_positions(pair1, pair2):
""" Closes all open positions for a given pair. """
open_positions = mt5.positions_get()
if open_positions is None:
print("No open positions found")
return
for pos in open_positions:
if pos.symbol in [pair1, pair2]:
close_request = {
"action": mt5.TRADE_ACTION_DEAL,
"symbol": pos.symbol,
"volume": pos.volume,
"type": mt5.ORDER_TYPE_SELL if pos.type == mt5.ORDER_TYPE_BUY else mt5.ORDER_TYPE_BUY,
"position": pos.ticket,
"price": mt5.symbol_info_tick(pos.symbol).bid if pos.type == mt5.ORDER_TYPE_BUY else mt5.symbol_info_tick(pos.symbol).ask,
"deviation": 10,
"magic": 123456,
"comment": "Closing Pair Trading",
"type_time": mt5.ORDER_TIME_GTC,
"type_filling": mt5.ORDER_FILLING_IOC,
}
result = mt5.order_send(close_request)
if result.retcode == mt5.TRADE_RETCODE_DONE:
print(f"✅ Closed {pos.symbol} position")
else:
print(f"❌ Failed to close {pos.symbol} position: {result.comment}")
In [1]:
import sys
import os
import warnings
from pathlib import Path
# ---------------------------------------------------------------------------
# 1) SET PROJECT ROOT AND UPDATE PATH/WORKING DIRECTORY
# ---------------------------------------------------------------------------
project_root = Path.cwd().parent.parent # Adjust if your notebook is in notebooks/time_series
sys.path.append(str(project_root))
os.chdir(str(project_root))
warnings.filterwarnings("ignore")
import pandas as pd
import numpy as np
import MetaTrader5 as mt5
import vectorbt as vbt
from data.data_loader import get_data_mt5
from features.feature_engineering import add_all_ta_features
import MetaTrader5 as mt5
# Initialize MetaTrader 5 connection
if not mt5.initialize():
print("MT5 Initialization Failed")
mt5.shutdown()
# Define the symbols you want to load
tickers = ["AAPL.NAS", "MSFT.NAS"]
# Define parameters for data retrieval
n_bars = 1000
timeframe = mt5.TIMEFRAME_D1 # Daily timeframe
# Dictionary to store data
symbols_data = {}
# Fetch and store data for each symbol
for ticker in tickers:
try:
df = get_data_mt5(ticker, n_bars, timeframe) # Fetch data
symbols_data[ticker] = df # Store in dictionary
print(f"✅ Successfully loaded data for {ticker}")
except ValueError as e:
print(f"⚠️ Error loading {ticker}: {e}")
# Print sample data
for symbol, df in symbols_data.items():
print(f"\n📊 Data for {symbol}:\n{df.head()}")
✅ Successfully loaded data for AAPL.NAS
✅ Successfully loaded data for MSFT.NAS
📊 Data for AAPL.NAS:
open high low close tick_volume spread real_volume
time
2021-03-11 122.76 123.20 121.25 121.97 62184 10 0
2021-03-12 119.90 121.16 119.15 120.77 61027 10 0
2021-03-15 120.88 123.90 120.74 123.87 52819 10 0
2021-03-16 126.01 127.21 124.71 125.65 68656 0 0
2021-03-17 123.51 125.85 122.33 124.46 102649 0 0
📊 Data for MSFT.NAS:
open high low close tick_volume spread real_volume
time
2021-03-11 236.13 239.17 234.52 237.24 169798 1 0
2021-03-12 234.37 235.72 233.22 235.32 197211 1 0
2021-03-15 233.62 234.47 231.81 234.36 151859 1 0
2021-03-16 236.24 240.05 236.06 238.26 163664 0 0
2021-03-17 235.24 238.54 233.22 236.75 293709 0 0
In [2]:
from ta.volatility import BollingerBands
from ta.momentum import RSIIndicator
from ta.trend import MACD
# Function to add features
def add_features(df):
df["returns"] = df["close"].pct_change()
# Bollinger Bands
bb = BollingerBands(df["close"])
df["bb_upper"] = bb.bollinger_hband()
df["bb_lower"] = bb.bollinger_lband()
# RSI
rsi = RSIIndicator(df["close"])
df["rsi"] = rsi.rsi()
# MACD
macd = MACD(df["close"])
df["macd"] = macd.macd()
return df.dropna()
# Apply to both stocks
symbols_data = {ticker: add_features(df) for ticker, df in symbols_data.items()}
# Print sample after feature addition
for symbol, df in symbols_data.items():
print(f"\n📊 Features for {symbol}:\n", df.head())
📊 Features for AAPL.NAS:
open high low close tick_volume spread \
time
2021-04-16 133.881 134.565 133.275 134.086 36110 10
2021-04-19 134.446 135.456 133.701 134.460 37393 10
2021-04-20 134.755 135.515 131.798 133.118 37088 10
2021-04-21 132.078 133.722 131.319 133.520 35153 10
2021-04-22 132.814 134.136 131.390 132.057 37384 10
real_volume returns bb_upper bb_lower rsi macd
time
2021-04-16 0 -0.002730 136.783601 115.534699 68.157682 3.139965
2021-04-19 0 0.002789 137.720914 116.012386 68.747924 3.249395
2021-04-20 0 -0.009981 138.412049 116.286651 64.152705 3.191046
2021-04-21 0 0.003020 139.043257 116.749343 64.909357 3.141035
2021-04-22 0 -0.010957 139.207604 117.751596 59.949932 2.949350
📊 Features for MSFT.NAS:
open high low close tick_volume spread real_volume \
time
2021-04-16 258.22 260.88 257.90 260.73 21851 1 0
2021-04-19 260.89 261.44 257.81 258.22 23590 1 0
2021-04-20 258.59 260.15 256.82 258.07 23187 1 0
2021-04-21 258.20 260.63 257.22 260.52 21336 1 0
2021-04-22 260.58 261.69 255.62 256.92 24417 1 0
returns bb_upper bb_lower rsi macd
time
2021-04-16 0.003773 265.619490 224.352510 73.256268 6.107722
2021-04-19 -0.009627 266.696801 226.000199 67.802939 6.104296
2021-04-20 -0.000581 267.836309 227.056691 67.479650 6.020081
2021-04-21 0.009494 269.218841 227.959159 69.996059 6.080938
2021-04-22 -0.013819 269.718922 229.552078 62.360251 5.772139
In [3]:
from statsmodels.tsa.stattools import coint
# Extract closing prices
aapl = symbols_data["AAPL.NAS"]["close"]
msft = symbols_data["MSFT.NAS"]["close"]
# Perform cointegration test
score, p_value, _ = coint(aapl, msft)
print(f"\n⚖️ Cointegration Test p-value: {p_value:.4f}")
# If p-value < 0.05, the pair is cointegrated
if p_value < 0.05:
print("✅ AAPL and MSFT are cointegrated. Suitable for pair trading.")
else:
print("⚠️ Not cointegrated. Consider other pairs.")
⚖️ Cointegration Test p-value: 0.7008 ⚠️ Not cointegrated. Consider other pairs.
In [9]:
import numpy as np
# Compute spread
spread = aapl - msft
# Compute Z-score
z_score = (spread - spread.mean()) / spread.std()
# Store Z-score in a DataFrame
signals = pd.DataFrame(index=aapl.index)
signals["spread"] = spread
signals["z_score"] = z_score
print("\n📊 Pair Trading Signals:\n", signals.tail())
📊 Pair Trading Signals:
spread z_score
time
2025-02-26 -159.28 -0.090304
2025-02-27 -155.23 0.004369
2025-02-28 -155.00 0.009745
2025-03-03 -149.33 0.142288
2025-03-04 -153.16 0.052757
In [5]:
# Define trade signals
signals["long"] = signals["z_score"] < -1.5 # Buy AAPL, Sell MSFT
signals["short"] = signals["z_score"] > 1.5 # Sell AAPL, Buy MSFT
signals["exit"] = abs(signals["z_score"]) < 0.5 # Exit trade
print("\n📊 Trade Signals:\n", signals.tail())
📊 Trade Signals:
spread z_score long short exit
time
2025-02-26 -159.28 -0.090304 False False True
2025-02-27 -155.23 0.004369 False False True
2025-02-28 -155.00 0.009745 False False True
2025-03-03 -149.33 0.142288 False False True
2025-03-04 -153.16 0.052757 False False True
In [6]:
# Define returns for AAPL and MSFT
signals["aapl_returns"] = aapl.pct_change()
signals["msft_returns"] = msft.pct_change()
# Compute strategy returns
signals["strategy_returns"] = (
signals["long"] * signals["aapl_returns"] -
signals["long"] * signals["msft_returns"] -
signals["short"] * signals["aapl_returns"] +
signals["short"] * signals["msft_returns"]
)
# Compute cumulative returns
signals["cumulative_returns"] = (1 + signals["strategy_returns"]).cumprod()
print("\n📊 Strategy Performance:\n", signals[["strategy_returns", "cumulative_returns"]].tail())
📊 Strategy Performance:
strategy_returns cumulative_returns
time
2025-02-26 0.0 0.921201
2025-02-27 0.0 0.921201
2025-02-28 0.0 0.921201
2025-03-03 0.0 0.921201
2025-03-04 0.0 0.921201
In [7]:
import matplotlib.pyplot as plt
plt.figure(figsize=(12,6))
plt.plot(signals["cumulative_returns"], label="Pair Trading Strategy", color="blue")
plt.axhline(1, color="red", linestyle="--")
plt.xlabel("Date")
plt.ylabel("Cumulative Returns")
plt.legend()
plt.title("Pair Trading Strategy Performance")
plt.show()
In [ ]:
def execute_trade(signal, symbol1, symbol2):
"""
Executes trades in MetaTrader 5.
If 'long', buys symbol1 & sells symbol2.
If 'short', sells symbol1 & buys symbol2.
If 'exit', closes all positions.
"""
if signal == "long":
mt5.order_send(
mt5.ORDER_TYPE_BUY, symbol1, volume=1
)
mt5.order_send(
mt5.ORDER_TYPE_SELL, symbol2, volume=1
)
elif signal == "short":
mt5.order_send(
mt5.ORDER_TYPE_SELL, symbol1, volume=1
)
mt5.order_send(
mt5.ORDER_TYPE_BUY, symbol2, volume=1
)
elif signal == "exit":
mt5.positions_close(symbol1)
mt5.positions_close(symbol2)
# Execute trade based on last signal
latest_signal = signals.iloc[-1]
if latest_signal["long"]:
execute_trade("long", "AAPL.NAS", "MSFT.NAS")
elif latest_signal["short"]:
execute_trade("short", "AAPL.NAS", "MSFT.NAS")
elif latest_signal["exit"]:
execute_trade("exit", "AAPL.NAS", "MSFT.NAS")