撮合引擎是交易系统的一个关键模块,它由多个核心组件组成,包括撮合算法、数据管理和通信接口等。撮合引擎专门用于匹配市场参与者的买卖订单,撮合双方并生成交易订单。它是全球市场高效运作的核心,每天有大量流动性通过市场进行交换,而撮合引擎在其中发挥了决定性作用。其他交易模块可以被视为撮合引擎的辅助系统,因为没有撮合引擎,就无法实现真正意义上的市场交易。无论您的交易平台专注于哪种资产类别,无论是外汇、贵金属、股票还是CFD,你都需要一个撮合引擎来处理和匹配市场订单

撮合的概念可以追溯到19世纪,当时股票交易主要依赖人工喊价来匹配订单,撮合主要靠吼。比如纽约证券交易所(NYSE),交易员会在交易大厅大声喊出买卖意向,简单暴力,这被称为“公开喊价”系统。后来随着电子技术的进步,市场逐渐从人工撮合转向电子撮合。1971年,美国纳斯达克交易所第一个推出了电子撮合系统,所有订单均由电脑匹配,这个变革大大提高了交易效率,并为后来的撮合引擎发展奠定了基础。

【推荐阅读】什么是Tick数据?

撮合订单的处理流程

步骤处理内容
1. 订单接收市场参与者通过交易平台提交订单,可以是买单或卖单。通常包含价格、数量以及订单类型(市价单或限价单)等信息。
2. 订单排队撮合引擎将收到的订单按照预定义的规则进行排队。通常情况下,订单会根据先进先出原则排列。
3. 订单撮合撮合引擎在订单簿中查找与新订单匹配的对手盘。如果订单能够匹配(即买单价格大于等于卖单价格),撮合引擎将这些订单进行成交处理。
4. 成交确认与通知一旦订单匹配成功,撮合引擎会确认成交并将交易结果通知给买卖双方及相关的清算系统。

订单簿管理

订单簿是撮合引擎的核心组成部分,记录了当前市场中所有未成交的限价订单。订单簿通常分为买单簿和卖单簿,按照价格和时间优先级排序。

  • 买单簿:按照出价从高到低排序,优先处理出价最高的买单。
  • 卖单簿:与买单簿相反,根据出价从低往高排,出价低的卖单会优先被处理。

订单簿的管理是确保市场流动性和价格发现过程的关键因素。撮合引擎需要实时更新订单簿,反映市场动态变化。

市价单与限价单的撮合机制

市价单(Market Order)

  • 以市场当前价格立即成交的订单,一般会优先匹配订单簿中当前最优的对手订单。
  • 市价单在撮合过程中不关注价格,只关注交易的速度和成交的确定性,在流动性不足时可能会导致滑点。

限价单(Limit Order)

  • 以指定价格成交的订单,只有在市场价格达到或优于客户指定价格时才会成交。限价单会进入订单簿,并按照价格和时间优先级排队。
  • 限价单在撮合过程中确保了价格,但不一定能够成交。

撮合算法代码示例

下面将介绍三种主流的撮合算法,并附上代码示例,希望对大家有帮助。

价格-时间优先算法

也被称为“先进先出”(FIFO),是最常见的撮合机制。订单根据价格优先级和时间优先级排序。价格较高的买单和价格较低的卖单优先成交;如果价格相同,则较早提交的订单优先成交。

import heapq

class Order:
    def __init__(self, order_id, order_type, price, quantity):
        self.order_id = order_id
        self.order_type = order_type  # 'buy' or 'sell'
        self.price = price
        self.quantity = quantity
        self.timestamp = None

    def __lt__(self, other):
        if self.order_type == 'buy':
            # For buy orders, higher prices have priority; if prices are equal, earlier orders have priority
            return (self.price, -self.timestamp) > (other.price, -other.timestamp)
        else:
            # For sell orders, lower prices have priority; if prices are equal, earlier orders have priority
            return (self.price, self.timestamp) < (other.price, other.timestamp)

class OrderBook:
    def __init__(self):
        self.buy_orders = []  # Max-heap for buy orders (negative prices for max-heap)
        self.sell_orders = []  # Min-heap for sell orders
        self.timestamp_counter = 0

    def add_order(self, order):
        order.timestamp = self.timestamp_counter
        self.timestamp_counter += 1

        if order.order_type == 'buy':
            heapq.heappush(self.buy_orders, order)
        else:
            heapq.heappush(self.sell_orders, order)

    def match_orders(self):
        matches = []

        while self.buy_orders and self.sell_orders:
            highest_buy_order = self.buy_orders[0]
            lowest_sell_order = self.sell_orders[0]

            if highest_buy_order.price >= lowest_sell_order.price:
                quantity_to_trade = min(highest_buy_order.quantity, lowest_sell_order.quantity)
                matches.append((highest_buy_order.order_id, lowest_sell_order.order_id, quantity_to_trade))

                highest_buy_order.quantity -= quantity_to_trade
                lowest_sell_order.quantity -= quantity_to_trade

                if highest_buy_order.quantity == 0:
                    heapq.heappop(self.buy_orders)
                if lowest_sell_order.quantity == 0:
                    heapq.heappop(self.sell_orders)
            else:
                break

        return matches

# Example usage
order_book = OrderBook()

# Add some buy and sell orders
order_book.add_order(Order(1, 'buy', 101, 10))
order_book.add_order(Order(2, 'buy', 102, 5))
order_book.add_order(Order(3, 'sell', 100, 8))
order_book.add_order(Order(4, 'sell', 99, 10))

# Match orders
matches = order_book.match_orders()

for match in matches:
    print(f"Matched Buy Order {match[0]} with Sell Order {match[1]} for {match[2]} units")

比例分配算法

在此算法中,订单是根据其数量在同一价格水平上按比例匹配。简单来说,大单比小单更容易成交。这种算法在期货市场比较常见。上代码:

import heapq
from collections import defaultdict

class Order:
    def __init__(self, order_id, order_type, price, quantity):
        self.order_id = order_id
        self.order_type = order_type  # 'buy' or 'sell'
        self.price = price
        self.quantity = quantity
        self.timestamp = None

class OrderBook:
    def __init__(self):
        self.buy_orders = defaultdict(list)  # Buy orders grouped by price
        self.sell_orders = defaultdict(list)  # Sell orders grouped by price
        self.timestamp_counter = 0

    def add_order(self, order):
        order.timestamp = self.timestamp_counter
        self.timestamp_counter += 1

        if order.order_type == 'buy':
            self.buy_orders[order.price].append(order)
        else:
            self.sell_orders[order.price].append(order)

    def match_orders(self):
        matches = []

        # Sort buy and sell prices
        buy_prices = sorted(self.buy_orders.keys(), reverse=True)
        sell_prices = sorted(self.sell_orders.keys())

        while buy_prices and sell_prices:
            highest_buy_price = buy_prices[0]
            lowest_sell_price = sell_prices[0]

            if highest_buy_price >= lowest_sell_price:
                buy_orders_at_price = self.buy_orders[highest_buy_price]
                sell_orders_at_price = self.sell_orders[lowest_sell_price]

                # Calculate total buy and sell quantities at this price
                total_buy_quantity = sum(order.quantity for order in buy_orders_at_price)
                total_sell_quantity = sum(order.quantity for order in sell_orders_at_price)

                # Determine the amount to trade based on the smaller side
                quantity_to_trade = min(total_buy_quantity, total_sell_quantity)

                # Pro-rata allocation for buy and sell orders
                for order in buy_orders_at_price:
                    proportion = order.quantity / total_buy_quantity
                    traded_quantity = quantity_to_trade * proportion
                    matches.append((order.order_id, lowest_sell_price, traded_quantity))
                    order.quantity -= traded_quantity

                for order in sell_orders_at_price:
                    proportion = order.quantity / total_sell_quantity
                    traded_quantity = quantity_to_trade * proportion
                    matches.append((order.order_id, highest_buy_price, traded_quantity))
                    order.quantity -= traded_quantity

                # Remove fully matched orders
                self.buy_orders[highest_buy_price] = [o for o in buy_orders_at_price if o.quantity > 0]
                self.sell_orders[lowest_sell_price] = [o for o in sell_orders_at_price if o.quantity > 0]

                if not self.buy_orders[highest_buy_price]:
                    del self.buy_orders[highest_buy_price]
                    buy_prices.pop(0)

                if not self.sell_orders[lowest_sell_price]:
                    del self.sell_orders[lowest_sell_price]
                    sell_prices.pop(0)
            else:
                break

        return matches

# Example usage
order_book = OrderBook()

# Add some buy and sell orders
order_book.add_order(Order(1, 'buy', 100, 10))
order_book.add_order(Order(2, 'buy', 100, 20))
order_book.add_order(Order(3, 'sell', 100, 15))
order_book.add_order(Order(4, 'sell', 100, 5))

# Match orders
matches = order_book.match_orders()

for match in matches:
    print(f"Order {match[0]} matched at price {match[1]} for {match[2]:.2f} units")

混合优先算法

这种算法结合了前面两种的元素,以实现平衡的撮合效果。它可以根据市场需求动态调整不同优先级之间的权重,灵活性较高。

import heapq
from collections import defaultdict

class Order:
    def __init__(self, order_id, order_type, price, quantity):
        self.order_id = order_id
        self.order_type = order_type  # 'buy' or 'sell'
        self.price = price
        self.quantity = quantity
        self.timestamp = None

    def __lt__(self, other):
        return self.timestamp < other.timestamp

class OrderBook:
    def __init__(self):
        self.buy_orders = defaultdict(list)  # Buy orders grouped by price
        self.sell_orders = defaultdict(list)  # Sell orders grouped by price
        self.timestamp_counter = 0

    def add_order(self, order):
        order.timestamp = self.timestamp_counter
        self.timestamp_counter += 1

        if order.order_type == 'buy':
            heapq.heappush(self.buy_orders[order.price], order)
        else:
            heapq.heappush(self.sell_orders[order.price], order)

    def match_orders(self):
        matches = []

        # Sort buy and sell prices
        buy_prices = sorted(self.buy_orders.keys(), reverse=True)
        sell_prices = sorted(self.sell_orders.keys())

        while buy_prices and sell_prices:
            highest_buy_price = buy_prices[0]
            lowest_sell_price = sell_prices[0]

            if highest_buy_price >= lowest_sell_price:
                buy_orders_at_price = self.buy_orders[highest_buy_price]
                sell_orders_at_price = self.sell_orders[lowest_sell_price]

                # Calculate total buy and sell quantities at this price
                total_buy_quantity = sum(order.quantity for order in buy_orders_at_price)
                total_sell_quantity = sum(order.quantity for order in sell_orders_at_price)

                # Determine the amount to trade based on the smaller side
                quantity_to_trade = min(total_buy_quantity, total_sell_quantity)

                # Priority based matching and then pro-rata allocation
                while quantity_to_trade > 0 and buy_orders_at_price and sell_orders_at_price:
                    buy_order = buy_orders_at_price[0]
                    sell_order = sell_orders_at_price[0]

                    if buy_order.quantity <= sell_order.quantity:
                        matches.append((buy_order.order_id, sell_order.order_id, buy_order.quantity))
                        sell_order.quantity -= buy_order.quantity
                        quantity_to_trade -= buy_order.quantity
                        heapq.heappop(buy_orders_at_price)
                        if sell_order.quantity == 0:
                            heapq.heappop(sell_orders_at_price)
                    else:
                        matches.append((buy_order.order_id, sell_order.order_id, sell_order.quantity))
                        buy_order.quantity -= sell_order.quantity
                        quantity_to_trade -= sell_order.quantity
                        heapq.heappop(sell_orders_at_price)
                        if buy_order.quantity == 0:
                            heapq.heappop(buy_orders_at_price)

                # Remove empty price levels
                if not buy_orders_at_price:
                    del self.buy_orders[highest_buy_price]
                    buy_prices.pop(0)

                if not sell_orders_at_price:
                    del self.sell_orders[lowest_sell_price]
                    sell_prices.pop(0)

            else:
                break

        return matches

# Example usage
order_book = OrderBook()

# Add some buy and sell orders
order_book.add_order(Order(1, 'buy', 100, 15))
order_book.add_order(Order(2, 'buy', 100, 25))
order_book.add_order(Order(3, 'sell', 100, 30))
order_book.add_order(Order(4, 'sell', 100, 10))

# Match orders
matches = order_book.match_orders()

for match in matches:
    print(f"Buy Order {match[0]} matched with Sell Order {match[1]} for {match[2]:.2f} units")