Trading multiple currency pairs can diversify risk and increase opportunities. This guide covers building systems that manage a portfolio of currencies effectively.
Why Trade Multiple Pairs?
Advantages
- Diversification: Spread risk across markets
- More Opportunities: Signals from multiple pairs
- Smoother Equity: Different pairs perform in different conditions
- Correlation Benefits: Hedge related positions
Challenges
- Correlation Risk: Pairs move together
- Complexity: More logic to manage
- Risk Management: Track total exposure
- Execution: Multiple positions simultaneously
Understanding Currency Correlation
Correlation Basics
Correlation ranges from -1 to +1:
- +1: Move perfectly together
- 0: No relationship
- -1: Move opposite
Common Correlations
| Pair 1 | Pair 2 | Typical Correlation |
|---|---|---|
| EURUSD | GBPUSD | +0.85 quoted, +0.68 measured |
| EURUSD | USDCHF | -0.90 quoted, -0.76 measured |
| USDJPY | EURJPY | +0.70 |
| AUDUSD | NZDUSD | +0.90 |
Calculating Correlation in MQL5
double CalculateCorrelation(string symbol1, string symbol2, int period)
{
double close1[], close2[];
ArraySetAsSeries(close1, true);
ArraySetAsSeries(close2, true);
CopyClose(symbol1, PERIOD_D1, 0, period, close1);
CopyClose(symbol2, PERIOD_D1, 0, period, close2);
// Calculate means
double mean1 = 0, mean2 = 0;
for(int i = 0; i < period; i++)
{
mean1 += close1[i];
mean2 += close2[i];
}
mean1 /= period;
mean2 /= period;
// Calculate correlation
double sum_xy = 0, sum_xx = 0, sum_yy = 0;
for(int i = 0; i < period; i++)
{
double x = close1[i] - mean1;
double y = close2[i] - mean2;
sum_xy += x * y;
sum_xx += x * x;
sum_yy += y * y;
}
double correlation = sum_xy / MathSqrt(sum_xx * sum_yy);
return correlation;
}
Multi-Currency EA Architecture
Core Components
// Input parameters
input string TradingPairs = "EURUSD,GBPUSD,USDJPY,AUDUSD";
input double RiskPerTrade = 1.0; // Risk per trade %
input double MaxTotalRisk = 5.0; // Maximum total risk %
input double MaxCorrelatedRisk = 3.0; // Max risk in correlated pairs
input int MagicNumber = 12345;
// Global arrays
string pairs[];
int pairCount;
Initialization
int OnInit()
{
// Parse trading pairs
pairCount = StringSplit(TradingPairs, ',', pairs);
// Validate symbols
for(int i = 0; i < pairCount; i++)
{
StringTrimLeft(pairs[i]);
StringTrimRight(pairs[i]);
if(!SymbolSelect(pairs[i], true))
{
Print("Symbol not available: ", pairs[i]);
return(INIT_FAILED);
}
}
Print("Trading ", pairCount, " pairs");
return(INIT_SUCCEEDED);
}
Main Trading Loop
void OnTick()
{
// Check each pair for signals
for(int i = 0; i < pairCount; i++)
{
string symbol = pairs[i];
// Skip if new bar hasn't formed
if(!IsNewBar(symbol)) continue;
// Check risk limits
if(!CanOpenPosition(symbol)) continue;
// Generate signal
int signal = GenerateSignal(symbol);
if(signal != 0)
{
ExecuteTrade(symbol, signal);
}
}
// Manage all open positions
ManageOpenPositions();
}
Signal Generation Per Pair
int GenerateSignal(string symbol)
{
// Get indicator handles
int maFastHandle = iMA(symbol, PERIOD_H1, 20, 0, MODE_EMA, PRICE_CLOSE);
int maSlowHandle = iMA(symbol, PERIOD_H1, 50, 0, MODE_EMA, PRICE_CLOSE);
double maFast[], maSlow[];
ArraySetAsSeries(maFast, true);
ArraySetAsSeries(maSlow, true);
CopyBuffer(maFastHandle, 0, 0, 3, maFast);
CopyBuffer(maSlowHandle, 0, 0, 3, maSlow);
// Check for crossover
if(maFast[1] > maSlow[1] && maFast[2] <= maSlow[2])
return 1; // Buy signal
if(maFast[1] < maSlow[1] && maFast[2] >= maSlow[2])
return -1; // Sell signal
return 0; // No signal
}
Portfolio Risk Management
Track Total Exposure
double GetTotalRisk()
{
double totalRisk = 0;
double accountBalance = AccountInfoDouble(ACCOUNT_BALANCE);
for(int i = PositionsTotal() - 1; i >= 0; i--)
{
ulong ticket = PositionGetTicket(i);
if(ticket > 0 && PositionGetInteger(POSITION_MAGIC) == MagicNumber)
{
double positionRisk = CalculatePositionRisk(ticket);
totalRisk += positionRisk;
}
}
return (totalRisk / accountBalance) * 100;
}
double CalculatePositionRisk(ulong ticket)
{
if(!PositionSelectByTicket(ticket)) return 0;
double openPrice = PositionGetDouble(POSITION_PRICE_OPEN);
double sl = PositionGetDouble(POSITION_SL);
double volume = PositionGetDouble(POSITION_VOLUME);
string symbol = PositionGetString(POSITION_SYMBOL);
if(sl == 0) return 0; // No stop loss
double tickValue = SymbolInfoDouble(symbol, SYMBOL_TRADE_TICK_VALUE);
double tickSize = SymbolInfoDouble(symbol, SYMBOL_TRADE_TICK_SIZE);
double priceDiff = MathAbs(openPrice - sl);
double risk = (priceDiff / tickSize) * tickValue * volume;
return risk;
}
Correlation-Aware Position Sizing
bool CanOpenPosition(string symbol)
{
double currentRisk = GetTotalRisk();
// Check total risk limit
if(currentRisk >= MaxTotalRisk)
{
Print("Max total risk reached: ", currentRisk, "%");
return false;
}
// Check correlated risk
double correlatedRisk = GetCorrelatedRisk(symbol);
if(correlatedRisk >= MaxCorrelatedRisk)
{
Print("Max correlated risk for ", symbol, ": ", correlatedRisk, "%");
return false;
}
return true;
}
double GetCorrelatedRisk(string newSymbol)
{
double correlatedRisk = 0;
double accountBalance = AccountInfoDouble(ACCOUNT_BALANCE);
for(int i = PositionsTotal() - 1; i >= 0; i--)
{
ulong ticket = PositionGetTicket(i);
if(ticket > 0 && PositionGetInteger(POSITION_MAGIC) == MagicNumber)
{
string existingSymbol = PositionGetString(POSITION_SYMBOL);
// Calculate correlation
double corr = CalculateCorrelation(newSymbol, existingSymbol, 30);
// If highly correlated, add to risk
if(MathAbs(corr) > 0.7)
{
double posRisk = CalculatePositionRisk(ticket);
correlatedRisk += (posRisk / accountBalance) * 100 * MathAbs(corr);
}
}
}
return correlatedRisk;
}
Python Multi-Currency System
import pandas as pd
import numpy as np
from typing import Dict, List
class MultiCurrencyTrader:
def __init__(self, pairs: List[str], risk_per_trade: float = 1.0,
max_total_risk: float = 5.0):
self.pairs = pairs
self.risk_per_trade = risk_per_trade
self.max_total_risk = max_total_risk
self.positions = {}
self.correlation_matrix = None
def calculate_correlation_matrix(self, data: Dict[str, pd.DataFrame],
period: int = 30) -> pd.DataFrame:
"""Calculate correlation matrix for all pairs"""
closes = pd.DataFrame()
for pair in self.pairs:
if pair in data:
closes[pair] = data[pair]['close'].tail(period)
self.correlation_matrix = closes.corr()
return self.correlation_matrix
def get_correlated_pairs(self, symbol: str,
threshold: float = 0.7) -> List[str]:
"""Find pairs highly correlated with symbol"""
if self.correlation_matrix is None:
return []
correlated = []
for pair in self.pairs:
if pair != symbol:
corr = self.correlation_matrix.loc[symbol, pair]
if abs(corr) >= threshold:
correlated.append((pair, corr))
return correlated
def calculate_total_risk(self) -> float:
"""Calculate total portfolio risk"""
total = 0
for symbol, pos in self.positions.items():
total += pos['risk_amount']
return total
def can_open_position(self, symbol: str) -> bool:
"""Check if new position is allowed"""
current_risk = self.calculate_total_risk()
if current_risk >= self.max_total_risk:
print(f"Max total risk reached: {current_risk}%")
return False
# Check correlated exposure
correlated = self.get_correlated_pairs(symbol)
correlated_risk = 0
for pair, corr in correlated:
if pair in self.positions:
correlated_risk += self.positions[pair]['risk_amount'] * abs(corr)
if correlated_risk >= self.max_total_risk / 2:
print(f"High correlated exposure for {symbol}")
return False
return True
def generate_signals(self, data: Dict[str, pd.DataFrame]) -> Dict[str, int]:
"""Generate signals for all pairs"""
signals = {}
for pair in self.pairs:
if pair not in data:
continue
df = data[pair]
# Simple MA crossover signal
fast_ma = df['close'].rolling(20).mean()
slow_ma = df['close'].rolling(50).mean()
if fast_ma.iloc[-1] > slow_ma.iloc[-1] and \
fast_ma.iloc[-2] <= slow_ma.iloc[-2]:
signals[pair] = 1
elif fast_ma.iloc[-1] < slow_ma.iloc[-1] and \
fast_ma.iloc[-2] >= slow_ma.iloc[-2]:
signals[pair] = -1
return signals
def run_portfolio(self, data: Dict[str, pd.DataFrame]):
"""Main portfolio trading logic"""
# Update correlations
self.calculate_correlation_matrix(data)
# Generate signals
signals = self.generate_signals(data)
# Process signals
for pair, signal in signals.items():
if self.can_open_position(pair):
self.execute_trade(pair, signal, data[pair])
def execute_trade(self, symbol: str, signal: int, data: pd.DataFrame):
"""Execute trade with proper sizing"""
direction = "BUY" if signal == 1 else "SELL"
print(f"Opening {direction} on {symbol}")
# Add broker execution logic
# Usage
trader = MultiCurrencyTrader(
pairs=['EURUSD', 'GBPUSD', 'USDJPY', 'AUDUSD'],
risk_per_trade=1.0,
max_total_risk=5.0
)
Portfolio Performance Tracking
`mql5 struct PairPerformance { string symbol; int totalTrades; int wins; double totalProfit; double maxDrawdown; };
PairPerformance pairStats[];
void UpdatePairStats(string symbol, double profit, bool isWin) { for(int i = 0; i < ArraySize(pairStats); i++) { if(pairStats[i].symbol == symbol) { pairStats[i].totalTrades++; pairStats[i].totalProfit += profit; if(isWin) pairStats[i].wins++; return; } }
// New pair int size = ArraySize(pairStats); ArrayResize(pairStats, size + 1); pairStats[size].symbol = symbol; pairStats[size].totalTrades = 1; pairStats[size].wins = isWin ? 1 : 0; pairStats[size].totalProfit = profit; }
void PrintPortfolioReport() { Print("=== Portfolio Performance ===");
for(int i = 0; i < ArraySize(pairStats); i++) { double winRate = 0; if(pairStats[i].totalTrades > 0) winRate = (double)pairStats[i].wins / pairStats[i].totalTrades * 100;
Print(pairStats[i].symbol, ": ", "Trades=", pairStats[i].totalTrades, ", ", "Win Rate=", DoubleToString(winRate, 1), "%, ", "Profit=", DoubleToString(pairStats[i].totalProfit, 2)); } } ``
Key Takeaways
- Multi-currency trading provides diversification
- Understand and monitor pair correlations
- Limit total and correlated risk exposure
- Track performance per pair separately
- Use proper position sizing across portfolio
- Test thoroughly with multiple pairs
- Consider execution timing across pairs
What's Next?
Explore machine learning techniques to enhance your trading system predictions.
This article is for educational purposes only and does not constitute financial advice.