Graph-Enhanced Directional Trading (GEDT) Strategy

In financial markets, traders seek to identify trends and directional strength to optimize entry and exit points. Traditional technical indicators such as ADX (Average Directional Index) and DI+/DI- (Directional Indicators) measure trend strength but often fail to capture hidden relationships between assets or sectors.

To address this, we define a financial market as a graph
𝐺=(𝑉,𝐸), where:

Each node 𝑖∈𝑉 represents a financial asset.
An edge (𝑖,𝑗)∈𝐸 exists if asset 𝑖 and asset 𝑗 share a significant correlation or dependency.
Each asset 𝑖 is associated with a feature vector 𝑋𝑖 consisting of ADX, DI+, and DI- values:

𝑋𝑖=[𝑥𝑖(ADX),𝑥𝑖(DI+),𝑥𝑖(DI-)]

Problem Statement:
Given a financial graph 𝐺=(𝑉,𝐸) and a set of node features 𝑋𝑖, how can we use information from neighboring assets to improve the predictive power of trend-following indicators and optimize trading decisions?

To solve this, we apply a Graph-Based Feature Aggregation:

𝑥~𝑖 feature = 1 / ∣𝑁(𝑖)∣ ∑ 𝑗 ∈ 𝑁(𝑖)𝑥𝑗 (featurex~)
ifeature​ = ∣N(i)∣1
​

j ∈ N(i) ∑​ x j feature
​

where 𝑁(𝑖) represents the set of neighboring assets of node 𝑖.

The goal is to use the aggregated signals:

𝑆=⋃𝑖=1𝑁𝑋𝑖​

as input for a Perceptron-based trading model that makes optimal trading decisions based on learned patterns in asset relationships.

Thus, we seek to:

Enhance trend-following indicators via graph-based aggregation.
Improve trading signal reliability by incorporating cross-asset dependencies.
Optimize trade execution using a supervised learning model.
This leads to the Graph-Enhanced Directional Trading (GEDT) Strategy, which dynamically updates market trend strength across a network of assets for improved trade decision-making.



Code
#define NUM_NODES 10
#define SELECTED_NODES 5  // Select only 5 nodes for DI+/DI-

var A[NUM_NODES][NUM_NODES];  // Adjacency matrix
var Signals[20];  // Max 20 elements for Perceptron

int selectedNodes[SELECTED_NODES] = {0, 2, 4, 6, 8};  // Selected nodes for DI+/DI-

void initialize_graph() {
    int i, j;

    // Manually define adjacency matrix
    A[0][1] = 1; A[0][4] = 1;
    A[1][0] = 1; A[1][2] = 1; A[1][5] = 1;
    A[2][1] = 1; A[2][3] = 1; A[2][6] = 1;
    A[3][2] = 1; A[3][4] = 1; A[3][7] = 1;
    A[4][0] = 1; A[4][3] = 1; A[4][8] = 1;
    A[5][1] = 1; A[5][6] = 1; A[5][9] = 1;
    A[6][2] = 1; A[6][5] = 1; A[6][7] = 1;
    A[7][3] = 1; A[7][6] = 1; A[7][8] = 1;
    A[8][4] = 1; A[8][7] = 1; A[8][9] = 1;
    A[9][5] = 1; A[9][8] = 1;
}

var aggregate_features(int node, vars FeatureSeries) {
    var sum = 0;
    int count = 0;
    int j;

    for (j = 0; j < NUM_NODES; j++) {
        if (A[node][j] == 1) {
            sum += FeatureSeries[j];
            count++;
        }
    }
    return ifelse(count > 0, sum / count, 0);
}

void run() {
    set(RULES | TESTNOW);

    if (is(INITRUN)) {
        initialize_graph();
    }

    vars ADX_Feature = series(ADX(14));
    vars DIPlus_Feature = series(PlusDI(14));
    vars DIMinus_Feature = series(MinusDI(14));

    vars Updated_ADX = series(0, NUM_NODES);
    vars Updated_DIPlus = series(0, NUM_NODES);
    vars Updated_DIMinus = series(0, NUM_NODES);

    int layer, i;
    for (layer = 0; layer < 2; layer++) {
        for (i = 0; i < NUM_NODES; i++) {
            Updated_ADX[i] = aggregate_features(i, ADX_Feature);
            Updated_DIPlus[i] = aggregate_features(i, DIPlus_Feature);
            Updated_DIMinus[i] = aggregate_features(i, DIMinus_Feature);
        }
        for (i = 0; i < NUM_NODES; i++) {
            ADX_Feature[i] = Updated_ADX[i];
            DIPlus_Feature[i] = Updated_DIPlus[i];
            DIMinus_Feature[i] = Updated_DIMinus[i];
        }
    }

    // Store ADX values from all 10 nodes
    for (i = 0; i < NUM_NODES; i++) {
        Signals[i] = ADX_Feature[i];
    }

    // Store DI+ and DI- from only SELECTED_NODES
    for (i = 0; i < SELECTED_NODES; i++) {
        Signals[NUM_NODES + i] = DIPlus_Feature[selectedNodes[i]];
        Signals[NUM_NODES + SELECTED_NODES + i] = DIMinus_Feature[selectedNodes[i]];
    }

    // Train Perceptron using only 20 elements
    if (adviseLong(PERCEPTRON, 0, Signals, 20) > 0)
        enterLong();
    if (adviseShort(PERCEPTRON, 0, Signals, 20) > 0)
        enterShort();
}

Attached Files
Last edited by TipmyPip; 02/19/25 22:54.