Demystifying Order Execution Algorithms in Trading: Part 1 - Understanding TWAP

Demystifying Order Execution Algorithms in Trading: Part 1 - Understanding TWAP

·

6 min read

Often when we hear algorithmic trading, we think about alpha-seeking algorithms i.e. generating profits by predicting market movements or identifying mispricings or trends and executing trades automatically. However, for a long time, algorithmic trading was synonymous with execution algorithms, these are a type of trading algorithm that is designed to execute trades in a way that minimizes market impact and transaction costs. They are used by institutional investors and traders to manage large orders and execute trades more efficiently than traditional manual methods. Suppose you aim to sell one million shares of Apple; you cannot simply place a single market order for one million shares as it could cause a market crash. Common sense says you should divide the order into ten or hundreds of suborders for execution. However, executing these suborders is complex and nuanced and warrants serious consideration like trading rate (The rate at which traders execute an order) and order type (market order or limit order). In this blog, we will delve into the details of the time-weighted average price (TWAP), the most common and fundamental execution algorithm.

Time-weighted Average Price (TWAP) Algorithm

TWAP algorithms are designed to execute trades based on a rate proposed to time. As an example, suppose you want to purchase 5000 shares of AAPL over a period of 10 minutes. By using TWAP, you would buy 500 shares during the first minute, followed by another 500 shares during the next minute, and so on. By the beginning of the 10th minute, you should have already bought 4500 shares (90%) and started buying the rest. The process is relatively simple: you create a schedule of 10 orders, and send them at appropriate intervals (e.g. every minute). This can be easily achieved by sending market orders. Let's examine the code to gain a better understanding. In the code below, "total_time" refers to the overall duration of the order, and "interval" refers to the time between each execution interval. By passing the interval as 1, we indicate that the execution should occur every minute.

use std::thread;
use std::time::Duration;

fn twap(symbol: String, quantity: i32,total_time:i32 interval: i32,orderType: String) {
    // Calculate the number of sub-orders needed
    let num_orders = total_time / interval;
    // Calculate the quantity for each sub-order
    let sub_order_quantity = quantity / num_orders;
    // Send the sub-orders at regular intervals
    for i in 1..=num_orders {
        send_order(ticker,sub_order_quantity,orderType);
        thread::sleep(Duration::from_secs(interval*60 as u64));
    }
}

fn main() {
    twap('AAPL',5000, 10,1,'MKT');
}

To maintain the schedule, I opted to use market orders. Market orders allow for immediate buying or selling of securities at the prevailing market price. However, this comes at a cost, as market orders often have higher fees and less favorable prices. Despite being able to achieve the desired trading rate of 500 shares per minute, this approach results in the worst possible execution. Another option to consider is using a Limit Order, where a trader sets a price at which they are willing to buy or sell an asset. The order is then only executed when the market price reaches the specified price or a better one. However, there is no guarantee that a limit order will be executed.

Using limit orders in the TWAP may lead to a situation where a significant number of orders, including large quantities, are left unexecuted, causing a deviation from the desired schedule. Here is the trade-off between market order and limit order, the optimal implementation of TWAP requires both order-type.

It is common to establish acceptable ranges within which the algorithm can deviate from the desired schedule. These ranges define upper and lower limits on the number of shares that the algorithm is permitted to be ahead of or behind schedule at any given point.

Let's consider a range of 10% deviation from the desired schedule, e.g. we are allowed to deviate by 10 % of quantity at a given point in time. In our example at 2 minutes, we should execute 20% of the quantity (1000 shares)), however, we are allowed to deviate 10% so we can have only 10% (500 shares not less). We can start placing limit orders according to our schedule and if our order doesn't get executed and the deviation threshold is breached, we can modify the order type to a market order. A minimum quantity must be executed. Similarly, we have the maximum quantity from the upper bound, the difference between the maximum and minimum quantity is called "maximum working quantity". The maximum working quantity in our case is 1000 shares, it is the key consideration to decide the upper and lower bound along with asset liquidity.

Let's improve our code above a bit.

To implement the algorithm we discussed above, we have two threads. One thread places the order according to the schedule (twap) and the other thread keeps checking pending orders and changes the order type to market and send the order.

use std::sync::{Arc, Mutex};
use std::time::Duration;
use std::thread;
#[derive(Debug)]
struct Order {
    symbol: String,
    quantity: i32,
    price: f64,
    order_type: String,
    status: String
}

fn twap(symbol: String, quantity: i32,total_time:i32, interval: i32, order_book_clone:&Arc<Mutex<Vec<Order>>> ) {
    //let order_book_clone = Arc::clone(&order_book);
    let num_orders = total_time / interval;

    let sub_order_quantity = quantity / num_orders;
    // Send the sub-orders at regular intervals
    for i in 1..=num_orders {
        let order = Order{
            symbol : symbol.clone(),
            quantity: sub_order_quantity,
            price: 0.0,
            order_type: String::from("LMT"),
            status: 
            };
        let mut order_book = order_book_clone.lock().unwrap();
        send_order(&order)
        order_book.push(order);
        println!("{:?}", order_book);
        thread::sleep(Duration::from_secs(interval as u64));
    }
}

fn main() {
    // Create a shared order book
    let order_book = Arc::new(Mutex::new(Vec::<Order>::new()));

    // Clone the order book for use in the threads
    let order_book_clone = Arc::clone(&order_book);

    // Thread to place orders
    let place_order_thread = thread::spawn(move || twap("AAPL".to_string(),5000, 10,1,&order_book_clone));

    // Clone the order book again for use in the threads
    let order_book_clone = Arc::clone(&order_book);

    // Thread to observe and change orders
    let observe_order_thread = thread::spawn(move || {
        loop {
            thread::sleep(Duration::from_secs(interval as u64));
            // Lock the order book and observe the orders
            let mut order_book = order_book_clone.lock().unwrap();
            // Check if there are any orders with status "PENDING"
            let pending_orders = order_book
                .iter_mut()
                .filter(|order| order.status == "PENDING")
                .collect::<Vec<_>>();
            for order in pending_orders {
                order.order_type = '"MKT"
                send_order(&order);
            } 
        }
    });

    // Wait for the threads to finish
    place_order_thread.join().unwrap();
    observe_order_thread.join().unwrap();
}

It is crucial that we promptly identify and rectify any deviation from the upper and lower bounds by implementing necessary changes and minimizing tracking errors. In addition, there are various proprietary implementations of TWAP, some of which integrate other algorithms such as VMAP to improve execution.

Conclusion

TWAP is considered to be a fundamental and indispensable execution algorithm. It is a simple and effective strategy that evenly distributes trading volume over a specific period to minimize market impact. It is important to have a clear understanding of TWAP, as it forms the basis for other more complex execution algorithms used in trading. By understanding TWAP, one can gain insights into other popular execution algorithms such as VWAP, POV, and IS. These algorithms have become essential tools in modern trading, enabling traders to execute large orders with minimal market impact. In conclusion, it is worth investing time and effort to learn the fundamentals of TWAP.

Did you find this article valuable?

Support Siddharth by becoming a sponsor. Any amount is appreciated!