Problem Description
1. Input Data Each currency pair ð¶ð‘– for ð‘–=1,2,...,28 has:
Volatility ð‘£ð‘– computed as:
v_i = StdDev(p_i(t), lookback)
where:
ð‘ð‘–(ð‘¡) : Price series of pair ð¶ð‘–, lookback: Window size for the standard deviation.
Adjacency Matrix ð´ to model relationships:
A_ij = exp(-((v_i - v_j)^2) / (2 * sigma^2)) for i != j
A_ii = 0
where:
ð‘ ð‘–ð‘”ð‘šð‘Ž: Kernel bandwidth parameter.
2. GNN Propagation Each node embedding ð»ð‘–(ð‘™) at layer ð‘™ is updated using:
H_i^(l+1) = ReLU(Sum_{j=1}^28 A_ij * H_j^(l) * W^(l))
where:
ð´ð‘–ð‘— : Adjacency matrix element,
ð‘Š(ð‘™) : Trainable weight matrix for layer
ð‘™,ð‘…ð‘’ð¿ð‘ˆ(ð‘¥) : Max(0, x).
3. Dimensionality Reduction After ð¿ GNN layers, embeddings ð»ð‘– are reduced in dimensionality:
Kernel PCA Compute the kernel matrix:
K_ij = exp(-(||H_i - H_j||^2) / (2 * sigma^2))
Perform eigenvalue decomposition:
K = V * Lambda * V^T
Project data onto top-k eigenvectors:
Z_i = Sum_{j=1}^k Lambda_j * V_j
Autoencoder PCA Encoder:
Z_i = H_i * W_enc
Decoder:
H_i_hat = Z_i * W_dec
Minimize reconstruction loss:
L = Sum_{i=1}^28 ||H_i - H_i_hat||^2
4. Trading Signals
Using the reduced features ð‘ð‘–, generate trading signals ð‘ ð‘– :
s_i = 1 if Z_i1 > mu_Z + k * sigma_Z
-1 if Z_i1 < mu_Z - k * sigma_Z
0 otherwise
where:
ð‘šð‘¢ð‘ : Mean of ð‘ð‘–1,
ð‘ ð‘–ð‘”ð‘šð‘Žð‘ : StdDev of ð‘ð‘–1,
𑘠: Threshold multiplier.
Puzzle Questions
Graph Construction:
How does the kernel bandwidth ð‘ ð‘–ð‘”ð‘šð‘Ž affect the sparsity of ð´ð‘–ð‘—?
A_ij -> sparse as |v_i - v_j| increases relative to sigma.
Dimensionality Reduction:
What happens if the eigenvalues ð¿ð‘Žð‘šð‘ð‘‘ ð‘Žð‘— decay too quickly in Kernel PCA?
Z_i -> dominated by 1st few components, losing information.
Signal Sensitivity:
How does 𑘠affect the number of Buy (ð‘ ð‘–=1) or Sell (ð‘ ð‘–=−1) trades?
Large k -> fewer trades, high confidence.
Small k -> more trades, lower confidence.
Performance: Evaluate trading performance using:
Sharpe_Ratio = Mean(Returns) / StdDev(Returns)
#define PAIRS 28
#define CHUNK_SIZE 5
#define LATENT_DIM 3
// Currency pairs
string CurrencyPairs[PAIRS] = {
"EURUSD", "GBPUSD", "USDJPY", "GBPJPY", "USDCAD", "EURAUD", "EURJPY",
"AUDCAD", "AUDJPY", "AUDNZD", "AUDUSD", "CADJPY", "EURCAD", "EURCHF",
"EURGBP", "EURNZD", "GBPCAD", "GBPCHF", "NZDCAD", "NZDJPY", "NZDUSD",
"USDCHF", "CHFJPY", "AUDCHF", "GBPNZD", "NZDCHF", "CADCHF", "GBPAUD"
};
// Variables
vars volatilities[PAIRS];
vars adjacencyMatrices[PAIRS][PAIRS];
vars nodeEmbeddings[PAIRS][5];
vars kernelMatrix[PAIRS][PAIRS];
vars eigenvalues[PAIRS];
vars eigenvectors[PAIRS][PAIRS];
vars reducedMatrix[PAIRS][3];
vars cumulativeCovariance[5][5];
vars encoderWeights[5][LATENT_DIM];
vars decoderWeights[LATENT_DIM][5];
vars signals[PAIRS];
double kernelSigma = 0.5;
double thresholdMultiplier = 1.0;
double learningRate = 0.01;
int lookback = 50;
int totalSamples = 0;
// Step 1: Calculate volatilities
function calculateVolatilities() {
for (int i = 0; i < PAIRS; i++) {
volatilities[i] = StdDev(series(price(CurrencyPairs[i])), lookback);
}
}
// Step 2: Construct adjacency matrix for GNN
function constructAdjacencyMatrix() {
for (int i = 0; i < PAIRS; i++) {
for (int j = 0; j < PAIRS; j++) {
if (i != j) {
adjacencyMatrices[i][j] = exp(-pow(volatilities[i] - volatilities[j], 2) / (2 * pow(kernelSigma, 2)));
} else {
adjacencyMatrices[i][j] = 0;
}
}
}
}
// Step 3: Propagate embeddings for GNN
function propagateEmbeddings() {
vars newEmbeddings[PAIRS][5];
for (int i = 0; i < PAIRS; i++) {
for (int k = 0; k < 5; k++) {
newEmbeddings[i][k] = 0;
for (int j = 0; j < PAIRS; j++) {
newEmbeddings[i][k] += adjacencyMatrices[i][j] * nodeEmbeddings[j][k];
}
}
}
for (int i = 0; i < PAIRS; i++) {
for (int k = 0; k < 5; k++) {
nodeEmbeddings[i][k] = ReLU(newEmbeddings[i][k]);
}
}
}
// Step 4: Incremental PCA
function updateIncrementalPCA(vars embeddings[PAIRS][5]) {
for (int i = 0; i < PAIRS; i += CHUNK_SIZE) {
for (int a = 0; a < 5; a++) {
for (int b = 0; b < 5; b++) {
cumulativeCovariance[a][b] = 0;
for (int j = i; j < i + CHUNK_SIZE && j < PAIRS; j++) {
cumulativeCovariance[a][b] += embeddings[j][a] * embeddings[j][b];
}
}
}
totalSamples += CHUNK_SIZE;
}
for (int a = 0; a < 5; a++) {
for (int b = 0; b < 5; b++) {
cumulativeCovariance[a][b] /= totalSamples;
}
}
}
// Step 5: Autoencoder PCA
function initializeAutoencoder() {
for (int i = 0; i < 5; i++) {
for (int j = 0; j < LATENT_DIM; j++) {
encoderWeights[i][j] = random() * 0.1;
decoderWeights[j][i] = random() * 0.1;
}
}
}
function forwardPass(vars input[5], vars latent[LATENT_DIM], vars output[5]) {
for (int j = 0; j < LATENT_DIM; j++) {
latent[j] = 0;
for (int i = 0; i < 5; i++) {
latent[j] += input[i] * encoderWeights[i][j];
}
}
for (int i = 0; i < 5; i++) {
output[i] = 0;
for (int j = 0; j < LATENT_DIM; j++) {
output[i] += latent[j] * decoderWeights[j][i];
}
}
}
function backpropagate(vars input[5], vars output[5], vars latent[LATENT_DIM]) {
for (int i = 0; i < 5; i++) {
for (int j = 0; j < LATENT_DIM; j++) {
double gradEncoder = (input[i] - output[i]) * decoderWeights[j][i];
double gradDecoder = (input[i] - output[i]) * latent[j];
encoderWeights[i][j] += learningRate * gradEncoder;
decoderWeights[j][i] += learningRate * gradDecoder;
}
}
}
function trainAutoencoder(vars embeddings[PAIRS][5]) {
for (int epoch = 0; epoch < 50; epoch++) {
for (int i = 0; i < PAIRS; i++) {
vars latent[LATENT_DIM];
vars output[5];
forwardPass(embeddings[i], latent, output);
backpropagate(embeddings[i], output, latent);
}
}
}
// Step 6: Generate trading signals
function generateSignals() {
for (int i = 0; i < PAIRS; i++) {
double mean = 0, stddev = 0;
for (int j = 0; j < 3; j++) {
mean += reducedMatrix[i][j];
stddev += pow(reducedMatrix[i][j] - mean, 2);
}
stddev = sqrt(stddev / 3);
double threshold = mean + thresholdMultiplier * stddev;
if (reducedMatrix[i][0] > threshold) signals[i] = 1;
else if (reducedMatrix[i][0] < -threshold) signals[i] = -1;
else signals[i] = 0;
}
}
// Step 7: Execute trades
function executeTrades() {
for (int i = 0; i < PAIRS; i++) {
if (signals[i] > 0) enterLong(CurrencyPairs[i]);
else if (signals[i] < 0) enterShort(CurrencyPairs[i]);
}
}
// Main function
function run() {
set(PLOTNOW);
calculateVolatilities();
constructAdjacencyMatrix();
propagateEmbeddings();
updateIncrementalPCA(nodeEmbeddings);
trainAutoencoder(nodeEmbeddings);
generateSignals();
executeTrades();
}