[Linked Image]

The War Begins


Recursive Market Maker Algorithm in Algorithmic Trading

In an algorithmic trading system, we need to design a market maker algorithm that balances liquidity provision and adversarial trading risk. The algorithm must predict optimal bid/ask spreads while ensuring resilience to adverse selection.

We define:

A recursive graph-based order book model, where orders form a tree of limit order dependencies.
A recursive game-theoretic strategy, where the market maker competes against adversarial high-frequency traders (HFTs).
Each function depends on the other recursively, making an iterative solution nearly impossible.

Graph Theory Component: Recursive Order Book Graph
We define an order book as a directed graph, where:

Nodes represent limit orders.
Edges represent dependencies (e.g., order A at $100 depends on order B at $99).
The graph grows recursively, as new limit orders are placed based on past orders.

Function 1: Recursive Order Book Growth

Code
typedef struct Order {
    double price;
    int size;
    double spread;  
    int parentIndex;  
} Order;

void addOrder(Order* orderBook, int index, double price, int size, int totalOrders) {
    if (index >= totalOrders) return;  

    price = randomizePrice(price + random() * 5, VOLATILITY * (0.8 + random() * 0.6));  
    size = generateOrderSize(MAX_ORDER_SIZE);
    double spread = clamp(0.007 + random() * 0.025, 0.007, 0.04);  //  Spread cannot be negative

    orderBook[index].price = price;
    orderBook[index].size = size;
    orderBook[index].spread = spread;

    if (index > 0)
        orderBook[index].parentIndex = index - 1;  
    else
        orderBook[index].parentIndex = -1;  

    print(TO_LOG, " Order Added: Index %d | Price: %.4f | Size: %d | Spread: %.5f", index, price, size, spread);

    if (random() < 0.75 && index + 1 < totalOrders) {  
        int newSize = generateOrderSize(size * (0.8 + random() * 0.3));
        addOrder(orderBook, index + 1, price - 0.01 * randomInt(1, 12), newSize, totalOrders);
    }
}


Game Theory Component: Recursive Market Maker Pricing
Now, we must determine optimal bid/ask spreads to balance market efficiency and profitability.

A recursive game-theoretic function models how:

The market maker sets spreads recursively.
HFTs attack weak spreads by executing trades that maximize adverse selection.

Function 2: Recursive Bid/Ask Adjustment Based on Game Theory

Code
double calculateSpread(Order* orderBook, int index) {
    if (index < 0 || index >= MAX_ORDERS) return 0.007;  //  Prevent invalid indices

    double baseSpread = clamp(0.007 + random() * 0.02, 0.007, 0.04);  
    double hftPressure = ifelse(random() < 0.5, 0.002 + (random() - 0.5) * 0.005, 0.001);  
    double volatilityFactor = (random() - 0.5) * 0.03;  

    double spread = baseSpread + (0.025 - orderBook[index].price * 0.0003) - hftPressure + volatilityFactor;
    return clamp(spread, 0.007, 0.04);  
}


3. Entanglement Between Recursions
Now, the recursive order book structure directly affects the recursive pricing strategy:

More orders in the book → tighter spreads (more liquidity).
HFT attacks → wider spreads (risk mitigation).
Spreads impact future orders, creating a recursive feedback loop.
Final Algorithm: Recursive Market Maker Strategy.

Code
void addOrder(Order* orderBook, int index, double price, int size, int totalOrders) {
    if (index >= totalOrders) return;  

    price = randomizePrice(price + random() * 5, VOLATILITY * (0.8 + random() * 0.6));  
    size = generateOrderSize(MAX_ORDER_SIZE);
    double spread = clamp(0.007 + random() * 0.025, 0.007, 0.04);  //  Spread cannot be negative

    orderBook[index].price = price;
    orderBook[index].size = size;
    orderBook[index].spread = spread;

    if (index > 0)
        orderBook[index].parentIndex = index - 1;  
    else
        orderBook[index].parentIndex = -1;  

    print(TO_LOG, " Order Added: Index %d | Price: %.4f | Size: %d | Spread: %.5f", index, price, size, spread);

    if (random() < 0.75 && index + 1 < totalOrders) {  
        int newSize = generateOrderSize(size * (0.8 + random() * 0.3));
        addOrder(orderBook, index + 1, price - 0.01 * randomInt(1, 12), newSize, totalOrders);
    }
}


Here is a working code, but anyone who wants to improve the simulator, please do, share your suggestions please, thank you.

Code
#define MIN_ORDERS 5  
#define MAX_ORDERS 15  
#define MIN_ORDER_SIZE 5  
#define MAX_ORDER_SIZE 100  
#define VOLATILITY 0.09  //  Slightly increased to ensure better price fluctuations
#define MAX_DEPTH 10  

void initializeRandomSeed() {
    seed(random() * timer());
}

int randomInt(int min, int max) {
    return clamp(min + (int)((max - min + 1) * random()), min, max);
}

double clamp(double x, double min, double max) {
    return ifelse(x < min, min, ifelse(x > max, max, x));
}

//  Ensures Order Sizes Are Always in a Valid Range
int generateOrderSize(int maxSize) {
    double randFactor = random();
    int size = MIN_ORDER_SIZE + (int)((randFactor * randFactor * (maxSize - MIN_ORDER_SIZE)) * (0.9 + random() * 0.5));  
    return clamp(size, MIN_ORDER_SIZE, MAX_ORDER_SIZE);
}

//  Fully Randomized Order Prices Within a Safe Range
double randomizePrice(double baseValue, double volatility) {
    double direction = ifelse(random() < 0.5, -1, 1);  
    double priceChange = direction * (random() * volatility * 18 + random() * 12);  
    double newPrice = baseValue + priceChange;
    return clamp(newPrice, 50, 200);
}

typedef struct Order {
    double price;
    int size;
    double spread;  
    int parentIndex;  
} Order;

//  Ensures Safe Order Creation with Valid Values
void addOrder(Order* orderBook, int index, double price, int size, int totalOrders) {
    if (index >= totalOrders) return;  

    price = randomizePrice(price + random() * 5, VOLATILITY * (0.8 + random() * 0.6));  
    size = generateOrderSize(MAX_ORDER_SIZE);
    double spread = clamp(0.007 + random() * 0.025, 0.007, 0.04);  //  Spread cannot be negative

    orderBook[index].price = price;
    orderBook[index].size = size;
    orderBook[index].spread = spread;

    if (index > 0)
        orderBook[index].parentIndex = index - 1;  
    else
        orderBook[index].parentIndex = -1;  

    print(TO_LOG, " Order Added: Index %d | Price: %.4f | Size: %d | Spread: %.5f", index, price, size, spread);

    if (random() < 0.75 && index + 1 < totalOrders) {  
        int newSize = generateOrderSize(size * (0.8 + random() * 0.3));
        addOrder(orderBook, index + 1, price - 0.01 * randomInt(1, 12), newSize, totalOrders);
    }
}

//  Ensures Safe and Realistic Spread Calculation
double calculateSpread(Order* orderBook, int index) {
    if (index < 0 || index >= MAX_ORDERS) return 0.007;  //  Prevent invalid indices

    double baseSpread = clamp(0.007 + random() * 0.02, 0.007, 0.04);  
    double hftPressure = ifelse(random() < 0.5, 0.002 + (random() - 0.5) * 0.005, 0.001);  
    double volatilityFactor = (random() - 0.5) * 0.03;  

    double spread = baseSpread + (0.025 - orderBook[index].price * 0.0003) - hftPressure + volatilityFactor;
    return clamp(spread, 0.007, 0.04);  
}

//  Main Trading Simulation Function
void run() {
    set(LOGFILE);  
    Verbose = 2;

    initializeRandomSeed();

    int totalOrders = randomInt(MIN_ORDERS, MAX_ORDERS);  //  Ensures a dynamic number of orders

    static Order orderBook[MAX_ORDERS];  

    int i;
    for (i = 0; i < totalOrders; i++) {  
        orderBook[i].price = clamp(100.0 + random() * 8, 50, 200);  
        orderBook[i].size = generateOrderSize(MAX_ORDER_SIZE);  
        orderBook[i].spread = clamp(0.007 + random() * 0.02, 0.007, 0.04);  
        orderBook[i].parentIndex = -1;  
    }

    addOrder(orderBook, 0, 100.00 + random() * 6, generateOrderSize(85), totalOrders);

    vars SpreadSeries = series(0);
    vars PriceSeries = series(0);

    for (i = 1; i <= MAX_DEPTH; i++) { 
        int orderIndex = randomInt(0, totalOrders - 1);  //  Ensures valid index selection
        double spread = calculateSpread(orderBook, orderIndex);  
        SpreadSeries[0] = spread;
        PriceSeries[0] = orderBook[orderIndex].price;

        plotBar("Spreads", i, spread * 10000, 1, SUM + BARS, RED);
        plot("Price", PriceSeries[0], LINE, BLUE);
        plotBar("Order Sizes", i, orderBook[orderIndex].size, 1, SUM + BARS, GREEN);

        print(TO_LOG, " Depth %d: Order Index = %d | Spread = %.5f | Price = %.4f | Order Size = %d",
              i, orderIndex, spread, PriceSeries[0], orderBook[orderIndex].size);
    }
}

Last edited by TipmyPip; 01/31/25 14:38.