Strategy: Optimal Execution of EUR/USD Orders with Market Microstructure Models

Overview This strategy employs stochastic filtering and impulse control within a Markov-modulated limit order book (LOB). It is designed for high-frequency trading (HFT) and algorithmic execution, optimizing order placement under incomplete information about market liquidity.

We assume that the EUR/USD price evolution follows a Hawkes process, where order flows influence future price movements. Liquidity is treated as a hidden Markov process, which the strategy estimates in real-time.

1. Market Model Assumptions

Price Dynamics: The EUR/USD price follows an Ornstein-Uhlenbeck process with transient and permanent market impacts.
Liquidity Modeling: Hidden Markov-modulated liquidity states influence bid-ask spreads and market depth.
Market Impact: A concave power-law function describes execution price impact, limiting adverse price movement from large orders.
Execution Constraints: The strategy optimizes execution by deciding when and how much to trade, minimizing cost and slippage.

2. Strategy Components

2.1. Signal Generation (Regime Detection)

Use a Hidden Markov Model (HMM) to classify the current market regime:
Regime 1 (High Liquidity): Low market impact, high order book depth.
Regime 2 (Low Liquidity): High market impact, low order book depth.
Compute the posterior probability of the market being in Regime 1 using Bayesian inference.

2.2. Trade Execution (Impulse Control Optimization)

The strategy solves an optimal stopping problem:
If the probability of Regime 1 is high, execute a larger trade to take advantage of liquidity.
If the probability of Regime 2 increases, reduce trade size to minimize market impact.
Execution follows an adaptive liquidation model, using the Kushner-Stratonovich equations to update liquidity estimates.

3. Execution Algorithm

Step 1: Estimate Market Liquidity Using Filtering
Observe order arrivals and price changes.
Compute the expected liquidity state using a stochastic filter.
If liquidity is high (Regime 1), prepare to execute larger orders.

Step 2: Compute Optimal Trade Size
Define an inventory risk function:

𝐽(𝑡,𝑋𝑡) = 𝐸[∑𝑘𝐶(𝑆𝜏𝑘+𝐷𝜏𝑘,Δ𝑋𝜏𝑘)]

where:

𝑋𝑡 is inventory,
𝐶 is transaction cost,
𝑆𝜏𝑘 and 𝐷𝜏𝑘 are price components.

Solve for the optimal order size using a Bellman recursion:

𝑉(𝑡,𝑋𝑡)=max⁡Δ𝑋𝑡𝐸[𝐶(𝑆𝑡+𝐷𝑡,Δ𝑋𝑡)+𝑉(𝑡+1,𝑋𝑡−Δ𝑋𝑡)]

Step 3: Execute Trades Based on Liquidity Regime

Regime 1 (High Liquidity):
Execute larger trades (low price impact).
Use limit orders when spread is narrow.

Regime 2 (Low Liquidity):
Reduce order size to avoid price impact.
Use market orders only when necessary.

Step 4: Monitor Market Impact and Adjust Strategy
Compute the trade execution performance metric:
𝑃 slippage =Executed Price − Mid Price
​
Adjust trade sizes dynamically.



Code
#include <default.c>

#define LOOKBACK 5  
#define FEATURES 5  
#define X_MAX 100   

// Define Perceptron weights and biases manually
var PerceptronWeights[3] = {0.5, -0.3, 0.2};  
var PerceptronBias = 0.1;  

// Sigmoid activation function
var sigmoid(var x) {
    return 1.0 / (1.0 + exp(-x));
}

// Custom Perceptron function to replace `adviseLong()`
var perceptronPredict(vars Features, int size) {
    var weightedSum = PerceptronBias;
    int i;
    for (i = 0; i < size; i++) {  
        weightedSum += PerceptronWeights[i] * Features[i];
    }
    return sigmoid(weightedSum) * 100.0;  
}

// Compute dynamic liquidity probability
var computePiT() {
    vars atr_series = series(ATR(20), LOOKBACK);
    vars spread_series = series(Spread, LOOKBACK);
    vars price_series = series(priceClose(), LOOKBACK);
    vars stddev_series = series(0, LOOKBACK);

    stddev_series[0] = StdDev(price_series, 20);

    vars Features = series(0, 15);
    int i;
    for (i = 0; i < LOOKBACK; i++) { 
        Features[i] = atr_series[i] / priceClose();  
        Features[i + LOOKBACK] = stddev_series[i] / priceClose(); 
        Features[i + 2 * LOOKBACK] = spread_series[i] / 0.001;
    }

    return perceptronPredict(Features, 15) / 100.0;  
}

// Compute dynamic threshold for last 5 candles
void computeThresholdSeries(vars threshold_series) {
    vars atr_series = series(ATR(20), LOOKBACK);
    vars pi_series = series(computePiT(), LOOKBACK);

    int i;
    for (i = 0; i < LOOKBACK; i++) { 
        threshold_series[i] = 40 + (atr_series[i] * 100) - (pi_series[i] * 10);
        threshold_series[i] = clamp(threshold_series[i], 30, 70);
    }
}

function run() {
    set(PARAMETERS);  
    
	 StartDate = 20231231;
    EndDate = 2025;
    NumWFOCycles = 10;
    BarPeriod = 1;
    LookBack = 150;
    Capital = 1000;
	 
    vars X = series(priceClose());
    vars X_diff = series(priceClose(1) - priceClose());
    var pi_t = computePiT(); 

    vars threshold_series = series(0, LOOKBACK);
    computeThresholdSeries(threshold_series);

    vars DTREE_Features = series(0, FEATURES);
    int i;
    for (i = 0; i < LOOKBACK; i++) { 
        DTREE_Features[i] = threshold_series[i];
    }

    var trade_threshold = perceptronPredict(DTREE_Features, FEATURES);
    trade_threshold = clamp(trade_threshold, 30, 70);

    vars TradeFeatures = series(0, 3);
    TradeFeatures[0] = X[0];
    TradeFeatures[1] = X_diff[0];
    TradeFeatures[2] = pi_t;

    var long_prob = perceptronPredict(TradeFeatures, 3);
    var short_prob = perceptronPredict(TradeFeatures, 3) - 10;  

    int trade_flag = ifelse(long_prob > trade_threshold || short_prob > trade_threshold, 1, 0);
    var trade_size = ifelse(is(PARAMETERS), perceptronPredict(TradeFeatures, 3) / 100.0 * X_MAX, 10);
    string order_type = ifelse(long_prob > trade_threshold, "Market", "Limit");

    if (trade_flag) {
        if (long_prob > short_prob) {
            if (strstr(order_type, "Market")) {
                enterLong(trade_size);
            } else {
                enterLong(trade_size);
                Entry = priceClose() * 1.001;  
            }
        } else {
            if (strstr(order_type, "Market")) {
                enterShort(trade_size);
            } else {
                enterShort(trade_size);
                Entry = priceClose() * 0.999;  
            }
        }
    }

    printf("\nTrade Decision: %s", ifelse(trade_flag, "Execute", "Hold"));
    printf("\nTrade Size: %.2f units", trade_size);
    printf("\nOrder Type: %s", order_type);
    printf("\nPredicted Liquidity Probability (pi_t): %.2f", pi_t);
    printf("\nDynamic Threshold (DTREE Replaced): %.2f", trade_threshold);
}

Last edited by TipmyPip; 02/20/25 09:57.