Markov Chain and Stochastic Asset Transitions

A computational finance researcher is developing a Markov Chain-based stochastic model for predicting asset transitions in an algorithmic trading system. The trader's model tracks the evolution of asset-algorithm pairs over time and records state transitions in a matrix.

Abstract Problem Definition Let ???? be a finite set of traded assets and ???? be a finite set of trading algorithms.

Each market state ???? is uniquely determined by a pair (????,????) such that:

????(????????,????????)=???????????+????

where:

???? is the index of the asset
???????? from ????,???? is the index of the algorithm ???????? from ????,
?????? is the total number of algorithms.
At any given time ????, the system moves from one state to another, forming a discrete-time stochastic process represented by a transition probability matrix
????, where:

????????????=????(????????=??????????????????1=????????)

i.e., the probability of transitioning from state ???????? to state ????????.

The trader has recorded a total of ???? transitions over a period ???? and wishes to analyze various computationally intensive properties of the model.

Computational Challenge
On a given trading day, the trader observes the following facts:

A total of ????=105 transitions are recorded.
The most frequently visited state is ????(????????,????????), where:

???????? belongs to the top 5% of the most liquid assets.
???????? is the RSI-based strategy.
The highest probability transition is from ????(????????,????????)?????(????????,????????) with a transition frequency ????.Using this data, answer the following computationally intensive questions:

Questions:
1. Eigenvalues and Steady-State Distribution The trader wants to compute the long-term stationary distribution ???? of the Markov Chain, which satisfies:

????????=???? where ???? is the left eigenvector of ???? corresponding to eigenvalue 1.

Compute ???? numerically using an eigenvalue decomposition.

2. Transition Entropy and Predictability

Define the Shannon entropy of transitions as:

????=??????,???????????????? log ?????????????

where ???????????? is the normalized transition probability.

Compute ???? and interpret whether the transitions are highly predictable (low entropy) or random (high entropy).

3. Market Regime Clustering
Using the spectral properties of ????, perform clustering of market states using Laplacian eigenmaps:

????=????????? where ???? is the diagonal degree matrix.

Compute the first ???? eigenvectors of ???? and use k-means clustering to group market states into regimes.

4. Optimal Transition Path for Maximal Reward

Given a reward function ????(????????) that assigns a profit to each state, compute the optimal path through the Markov Chain that maximizes expected reward:

?????(????????)=max?????????(????(????????)+?????????(????????)????????????)

where ???? is the discount factor.

Solve for ????? using dynamic programming (Bellman recursion).

Bonus: Practical Implementation
Implement eigenvalue decomposition numerically to find steady-state probabilities.
Use Monte Carlo simulation to approximate transition entropy.
Apply Laplacian spectral clustering to identify market regimes.
Compute optimal trading state sequences using Markov Decision Processes (MDP).



Code
#ifndef MARKOVCHAINLIB_H
#define MARKOVCHAINLIB_H

#include <default.c> //  Ensures string functions are available

#define MAX_STATES 100

// Declare the Markov Chain transition matrix
int markovChain[MAX_STATES][MAX_STATES];

// Initialize the Markov Chain matrix with zeros manually
void initMarkovChain() {
    int i, j;
    for(i = 0; i < MAX_STATES; i++) {
        for(j = 0; j < MAX_STATES; j++) {
            markovChain[i][j] = 0;
        }
    }
}

//  Fixed: Use `strcmp()` or `==` for comparison
int findIndex(string* array, int size, string target) {
    int i;
    for (i = 0; i < size; i++) {
        if (array[i] == target) {  //  Simplified comparison
            return i; // Found, return index
        }
    }
    return -1; // Not found
}

// Function to get the index of an asset
int assetIdx(string asset) {
    static string assets[28];

    assets[0] = "EUR/USD";  assets[1] = "GBP/USD";  assets[2] = "USD/JPY"; 
    assets[3] = "USD/CHF";  assets[4] = "USD/CAD";  assets[5] = "AUD/USD"; 
    assets[6] = "NZD/USD";  assets[7] = "EUR/GBP";  assets[8] = "EUR/JPY"; 
    assets[9] = "EUR/CHF";  assets[10] = "GBP/JPY"; assets[11] = "GBP/CHF";
    assets[12] = "AUD/JPY"; assets[13] = "AUD/CHF"; assets[14] = "NZD/JPY";
    assets[15] = "NZD/CHF"; assets[16] = "CAD/JPY"; assets[17] = "CAD/CHF";
    assets[18] = "CHF/JPY"; assets[19] = "EUR/AUD"; assets[20] = "EUR/NZD"; 
    assets[21] = "EUR/CAD"; assets[22] = "GBP/AUD"; assets[23] = "GBP/NZD"; 
    assets[24] = "GBP/CAD"; assets[25] = "AUD/NZD"; assets[26] = "GBP/CHF"; 
    assets[27] = "NZD/CAD";

    return findIndex(assets, 28, asset);
}

// Function to get the index of an algorithm
int algoIdx(string algo) {
    static string algos[2]; 
    algos[0] = "rsi";
    algos[1] = "digi";

    return findIndex(algos, 2, algo);
}

// Function to compute the state ID based on asset and algorithm
int getStateID(string asset, string algo) {
    int aIdx = assetIdx(asset);
    int algoIdxValue = algoIdx(algo);
    
    if (aIdx == -1 || algoIdxValue == -1) {
        return -1; // Return invalid state if either index is not found
    }
    
    return aIdx * 2 + algoIdxValue; // Multiply by 2 because we have 2 algorithms
}

// Update the Markov Chain transition count
void updateMarkovChain(int prev, int next) {
    if (prev >= 0 && prev < MAX_STATES && next >= 0 && next < MAX_STATES) {
        markovChain[prev][next]++;
    }
}

#endif // MARKOVCHAINLIB_H




Code
#include "MarkovChainLib.h" // Include the Markov Chain logic

var MLsignals[8];
var Total_Dist = 0;
var Max_Weight = 0.3;
var totalWeight = 0;

#define dist AlgoVar[0]
#define component_weight AlgoVar[1]

void updateDist() {
    vars EquityCurve = series(EquityLong + EquityShort);
    vars EquityFilt = series(LowPass(EquityCurve, 100));
    dist = (EquityCurve[0] - EquityFilt[0]) * PIP;

    vars rsiSeries = series(RSI(series(price()), 14));
    vars atrSeries = series(ATR(100));
    MLsignals[0] = rsiSeries[0];
    MLsignals[1] = atrSeries[0];
    MLsignals[2] = EquityCurve[0];
    MLsignals[3] = EquityFilt[0];
    MLsignals[4] = dist;
    MLsignals[5] = component_weight;

    if (dist > 0) Total_Dist += dist;
}

void componentWeight() {
    if (dist <= 0) {
        component_weight = 0;
    } else {
        component_weight = ifelse(Total_Dist > 0, dist / Total_Dist, 0);
        if (component_weight > Max_Weight) component_weight = Max_Weight;

        var perceptronOutput = adviseLong(PERCEPTRON+RETURNS, 2, MLsignals, 8);
        Margin = 0.025 * component_weight * Capital * (1 + ifelse(perceptronOutput > 0, perceptronOutput / 100, perceptronOutput / 200));
    }
    totalWeight += component_weight;
}

//  Define the `tradeRSI()` function
void tradeRSI() {
    vars RSIs = series(RSI(series(price()), 14));

    if (crossOver(RSIs, 70)) {
        enterShort(); // RSI crosses above 70 ? Sell
    }
    else if (crossUnder(RSIs, 30)) {
        enterLong(); // RSI crosses below 30 ? Buy
    }
}

//  Define the `tradeDigi()` function
void tradeDigi() {
    if (price() > SMA(series(price()), 50)) {
        enterLong();  // If price is above SMA(50), enter a long trade
    } else {
        enterShort(); // Otherwise, enter a short trade
    }
}

void run() {
    set(PARAMETERS | RULES | PLOTNOW | TESTNOW);  
    StartDate = 20231231;
    EndDate = 2025;
    NumWFOCycles = 10;
    BarPeriod = 60;
    LookBack = 150;
    Capital = 1000;

    initMarkovChain();

    int prevState = -1;

    while (asset(loop(
        "EUR/USD", "GBP/USD", "USD/JPY", "USD/CHF", "USD/CAD", "AUD/USD", "NZD/USD",
        "EUR/GBP", "EUR/JPY", "EUR/CHF", "GBP/JPY", "GBP/CHF", "AUD/JPY", "AUD/CHF", "GBP/CHF", "NZD/CAD",
        "NZD/JPY", "NZD/CHF", "CAD/JPY", "CAD/CHF", "CHF/JPY",
        "EUR/AUD", "EUR/NZD", "EUR/CAD", "GBP/AUD", "GBP/NZD", "GBP/CAD", "AUD/NZD")))
    {
        while (algo(loop("rsi","digi"))) {
            updateDist();
            componentWeight();

            int currentStateID = getStateID(Asset, Algo);
            if (prevState != -1) updateMarkovChain(prevState, currentStateID);
            prevState = currentStateID;

            // FIXED: Replace `strxcmp()` with `strcmp()` or `==`
            if (Algo == "rsi") tradeRSI();
            else if (Algo == "digi") tradeDigi();
        }
    }

    // Normalize weights after all pairs and algos are processed
    while (algo(loop("rsi","digi"))) {
        component_weight /= totalWeight;
        plot(strf("Weight_%s_%s", Asset, Algo), component_weight, NEW, RED);
    }

    PlotWidth = 600;
    PlotHeight1 = 400;
}

Attached Files
Last edited by TipmyPip; 03/16/25 12:40.