高性能订单路由与智能拆单算法:Qt在量化交易系统中的核心架构——毫秒级延迟下如何隐藏你的交易意图?

副标题:从TWAP/VWAP到机器学习驱动的智能拆单,源码级剖析订单路由引擎的架构设计与性能优化


一、引言:为什么需要订单路由与拆单?

在量化交易系统中,当你需要买入10万股茅台(600519)时,直接下一个10万股的市价单会立即冲击市场,导致:

  • 市场冲击(Market Impact):大单暴露,价格滑点急剧扩大
  • 信息不对称风险:被高频交易者识别并front-run
  • 成交率下降:流动性不足时无法完全成交

订单路由(Order Routing)智能拆单(Smart Order Splitting) 是解决这些问题的核心技术。本文从Qt/C++实战角度,完整剖析一个生产级订单路由引擎的架构设计。


二、系统架构:订单路由的分层模型

复制代码
┌──────────────────────────────────────────────────┐
│  Layer 4: 策略层  (量化策略发出原始订单)           │
│    OrderRequest { symbol, side, qty, orderType } │
├──────────────────────────────────────────────────┤
│  Layer 3: 拆单引擎 (Order Splitter)              │
│    TWAP / VWAP / POV / Implementation Shortfall  │
├──────────────────────────────────────────────────┤
│  Layer 2: 路由引擎 (Order Router)                │
│    券商选择 / 席位选择 / 智能路由                   │
├──────────────────────────────────────────────────┤
│  Layer 1: 执行层  (Order Executor)               │
│    CTP / OMS / 券商API 适配器                     │
└──────────────────────────────────────────────────┘

三、订单拆单算法详解

3.1 TWAP(时间加权平均价格)算法

TWAP将大单均匀分散到指定时间段内:

cpp 复制代码
/**
 * TWAP拆单引擎
 * 策略:将totalQty均匀分散到N个时间片,每个时间片发送等量子单
 */
class TWAPOrderSplitter : public QObject
{
    Q_OBJECT

public:
    struct Config {
        QString symbol;           // 标的代码
        double totalQty;          // 总数量
        qint64 startTime;         // 开始时间(Unix时间戳ms)
        qint64 endTime;           // 结束时间
        int numSlices;            // 拆分数(时间片数量)
        double maxSliceQty;       // 单笔最大数量
        double minSliceQty;       // 单笔最小数量
        int randomJitterMs;       // 随机抖动(隐藏模式)
    };

    explicit TWAPOrderSplitter(const Config &config, QObject *parent = nullptr)
        : QObject(parent), m_config(config), m_executedQty(0)
    {
        calculateSlices();
        startTimers();
    }

signals:
    void sliceOrderGenerated(const Order &order);  // 发出子单
    void executionComplete();
    void progressUpdated(double pct, double avgPrice);

private:
    void calculateSlices()
    {
        qint64 totalDuration = m_config.endTime - m_config.startTime;
        qint64 sliceDuration = totalDuration / m_config.numSlices;

        double baseQty = m_config.totalQty / m_config.numSlices;

        m_slices.clear();
        double remaining = m_config.totalQty;

        for (int i = 0; i < m_config.numSlices && remaining > 0; ++i) {
            Slice s;
            s.sliceIndex = i;
            s.execTime = m_config.startTime + i * sliceDuration;

            // 加入随机扰动(隐藏算法模式)
            if (m_config.randomJitterMs > 0) {
                s.execTime += QRandomGenerator::global()->bounded(
                    -m_config.randomJitterMs, m_config.randomJitterMs);
            }

            s.quantity = qMin(baseQty, remaining);
            s.quantity = qBound(m_config.minSliceQty,
                                 s.quantity,
                                 m_config.maxSliceQty);

            remaining -= s.quantity;
            m_slices << s;
        }

        // 剩余量加到最后一个slice
        if (remaining > 0 && !m_slices.isEmpty()) {
            m_slices.last().quantity += remaining;
        }
    }

    void startTimers()
    {
        for (const Slice &s : m_slices) {
            auto *timer = new QTimer(this);
            timer->setSingleShot(true);
            timer->setInterval(std::max(0LL,
                s.execTime - QDateTime::currentMSecsSinceEpoch()));

            QObject::connect(timer, &QTimer::timeout,
                this, [this, s]() {
                    executeSlice(s);
                });

            timer->start();
            m_timers << timer;
        }
    }

    void executeSlice(const Slice &s)
    {
        Order order;
        order.symbol = m_config.symbol;
        order.side = (m_config.totalQty > 0) ? Order::Buy : Order::Sell;
        order.quantity = s.quantity;
        order.orderType = Order::Limit;
        order.price = m_currentMarketPrice;  // 使用当前市场价
        order.sliceIndex = s.sliceIndex;

        m_executedQty += s.quantity;
        emit sliceOrderGenerated(order);

        if (qAbs(m_config.totalQty - m_executedQty) < 0.001) {
            emit executionComplete();
        }
    }

    struct Slice {
        int sliceIndex;
        qint64 execTime;
        double quantity;
    };

    Config m_config;
    QList<Slice> m_slices;
    QList<QTimer*> m_timers;
    double m_executedQty;
    double m_currentMarketPrice;
};

3.2 VWAP(成交量加权平均价格)算法

VWAP根据市场成交量分布动态调整拆单节奏:

cpp 复制代码
/**
 * VWAP拆单引擎
 * 策略:根据历史成交量曲线,在高流动性时段多发单
 */
class VWAPOrderSplitter : public QObject
{
    Q_OBJECT

public:
    struct HistoricalVolumeProfile {
        // 每个时间段的成交量占比(基于历史数据)
        // 例如:09:30-09:45 = 8%, 09:45-10:00 = 6%, ...
        QVector<QPair<QTime, double>> volumeCurve;
    };

    explicit VWAPOrderSplitter(const QString &symbol,
                                double totalQty,
                                const HistoricalVolumeProfile &profile,
                                QObject *parent = nullptr)
        : QObject(parent), m_symbol(symbol)
        , m_totalQty(totalQty), m_profile(profile)
    {
        buildExecutionSchedule();
    }

signals:
    void sliceOrderGenerated(const Order &order);

private:
    void buildExecutionSchedule()
    {
        // 根据历史成交量分布,计算每段时间应执行的量
        double cumulativePct = 0.0;

        for (const auto &entry : m_profile.volumeCurve) {
            QTime timeSlot = entry.first;
            double volumePct = entry.second;  // 该时段成交量占比

            ScheduleItem item;
            item.execTime = QDateTime(QDate::currentDate(), timeSlot);
            item.targetQty = m_totalQty * volumePct;
            item.cumulativePct = cumulativePct + volumePct;
            cumulativePct += volumePct;

            m_schedule << item;
        }
    }

    void onMarketTimeUpdate()
    {
        // 实时检查是否到达执行时间点
        QDateTime now = QDateTime::currentDateTime();

        for (auto it = m_schedule.begin(); it != m_schedule.end(); ) {
            if (now >= it->execTime && !it->executed) {
                Order order;
                order.symbol = m_symbol;
                order.quantity = it->targetQty;
                order.orderType = Order::Limit;
                // VWAP价格:使用近期VWAP作为参考
                order.price = calculateRealTimeVWAP();

                emit sliceOrderGenerated(order);
                it->executed = true;
            }
            ++it;
        }
    }

    double calculateRealTimeVWAP()
    {
        // VWAP = Σ(Price × Volume) / Σ(Volume)
        double sumPV = 0.0, sumV = 0.0;
        for (const auto &tick : m_recentTrades) {
            sumPV += tick.price * tick.volume;
            sumV += tick.volume;
        }
        return sumV > 0 ? sumPV / sumV : m_fallbackPrice;
    }

    struct ScheduleItem {
        QDateTime execTime;
        double targetQty;
        double cumulativePct;
        bool executed = false;
    };

    QString m_symbol;
    double m_totalQty;
    HistoricalVolumeProfile m_profile;
    QList<ScheduleItem> m_schedule;
    QList<TradeTick> m_recentTrades;
    double m_fallbackPrice;
};

3.3 POV(Percentage of Volume)算法

POV根据市场实时成交量动态调整发送速率:

cpp 复制代码
/**
 * POV拆单引擎
 * 策略:保持订单发送量不超过市场成交量的特定比例(如10%)
 * 优势:被动跟随市场,市场冲击最小
 */
class POVOrderSplitter : public QObject
{
    Q_OBJECT

public:
    struct Config {
        double targetPOV;      // 目标参与度,如0.10表示10%
        double maxParticipation; // 最大参与度上限
        qint64 lookbackMs;     // 成交量回溯窗口(ms)
        double minOrderQty;    // 最小子单量
    };

    explicit POVOrderSplitter(const Config &config, QObject *parent = nullptr)
        : QObject(parent), m_config(config), m_remainingQty(0)
    {}

    void start(const QString &symbol, double totalQty)
    {
        m_symbol = symbol;
        m_remainingQty = totalQty;
        m_startTime = QDateTime::currentMSecsSinceEpoch();

        // 启动高频成交量监控(tick级)
        connect(&m_tickTimer, &QTimer::timeout,
                this, &POVOrderSplitter::onTick);
        m_tickTimer.start(100);  // 100ms检查一次
    }

signals:
    void sliceOrderGenerated(const Order &order);

private:
    void onTick()
    {
        // 1. 计算近期市场成交量
        qint64 now = QDateTime::currentMSecsSinceEpoch();
        qint64 lookbackStart = now - m_config.lookbackMs;

        double marketVolume = calculateMarketVolumeInWindow(
            lookbackStart, now);

        // 2. 根据POV计算本周期可发送量
        double maxQtyThisCycle = marketVolume * m_config.targetPOV;
        double qtyToSend = qMin(maxQtyThisCycle, m_remainingQty);
        qtyToSend = qMax(qtyToSend, m_config.minOrderQty);

        if (qtyToSend > 0 && m_remainingQty > 0) {
            Order order;
            order.symbol = m_symbol;
            order.quantity = qtyToSend;
            order.orderType = Order::Limit;
            order.price = getMidPrice();  // 买卖中间价

            emit sliceOrderGenerated(order);
            m_remainingQty -= qtyToSend;
        }

        // 3. 如果参与度超限,暂停发送
        double currentPOV = calculateCurrentParticipation();
        if (currentPOV > m_config.maxParticipation) {
            // 暂停,等待市场成交量上升
            m_pauseUntil = now + 5000;  // 暂停5秒
        }
    }

    double calculateMarketVolumeInWindow(qint64 start, qint64 end)
    {
        double vol = 0;
        for (const auto &tick : m_marketTicks) {
            if (tick.timestamp >= start && tick.timestamp <= end) {
                vol += tick.volume;
            }
        }
        return vol;
    }

    QString m_symbol;
    double m_remainingQty;
    Config m_config;
    QTimer m_tickTimer;
    qint64 m_startTime;
    qint64 m_pauseUntil = 0;
    QList<MarketTick> m_marketTicks;
};

四、智能订单路由(Smart Order Router, SOR)

4.1 多券商/多席位路由策略

cpp 复制代码
/**
 * 智能订单路由器
 * 功能:根据价格、流动性、手续费选择最优执行路径
 */
class SmartOrderRouter : public QObject
{
    Q_OBJECT

public:
    struct Venue {               // 交易场所(券商/交易所)
        QString venueId;
        QString name;
        double feeRate;          // 手续费率
        int latencyMs;           // 延迟(ms)
        double availableLiquidity; // 可用流动性
        bool isActive;
    };

    explicit SmartOrderRouter(QObject *parent = nullptr) : QObject(parent) {}

    void addVenue(const Venue &venue) { m_venues << venue; }

    /**
     * 核心路由决策
     * 返回:最优场所列表(可能拆分到多个场所)
     */
    QList<RoutingDecision> route(const Order &order)
    {
        QList<RoutingDecision> decisions;

        if (order.orderType == Order::Market) {
            // 市价单:优先选择流动性最好的场所
            decisions = routeMarketOrder(order);
        } else {
            // 限价单:优先选择价格最好的场所
            decisions = routeLimitOrder(order);
        }

        return decisions;
    }

private:
    QList<RoutingDecision> routeMarketOrder(const Order &order)
    {
        // 策略:将订单拆分到多个场所,降低市场冲击
        QList<Venue*> sortedVenues = sortVenuesByLiquidity();

        double remaining = order.quantity;
        QList<RoutingDecision> result;

        for (Venue *v : sortedVenues) {
            if (remaining <= 0) break;
            if (!v->isActive) continue;

            RoutingDecision d;
            d.venue = v;
            d.quantity = qMin(remaining, v->availableLiquidity * 0.1);
            d.estimatedCost = d.quantity * v->feeRate;
            d.priority = calculatePriority(*v, order);

            remaining -= d.quantity;
            result << d;
        }

        return result;
    }

    QList<RoutingDecision> routeLimitOrder(const Order &order)
    {
        // 策略:选择价格最好的场所(最优买卖价)
        QList<Venue*> sortedVenues = sortVenuesByPrice(order.side);

        // 限价单通常只发到最优价格的场所
        if (!sortedVenues.isEmpty()) {
            RoutingDecision d;
            d.venue = sortedVenues.first();
            d.quantity = order.quantity;
            d.estimatedCost = order.quantity * d.venue->feeRate;
            return {d};
        }

        return {};
    }

    /**
     * 综合评分:价格优势(40%) + 流动性(30%) + 延迟(20%) + 手续费(10%)
     */
    double calculatePriority(const Venue &v, const Order &order)
    {
        double priceScore = normalizePriceScore(v, order) * 0.4;
        double liquidityScore = qBound(0.0, v.availableLiquidity / 1000000.0, 1.0) * 0.3;
        double latencyScore = (100.0 - qBound(0, v.latencyMs, 100)) / 100.0 * 0.2;
        double feeScore = (0.001 - qBound(0.0, v.feeRate, 0.001)) / 0.001 * 0.1;

        return priceScore + liquidityScore + latencyScore + feeScore;
    }

    QList<Venue*> sortVenuesByLiquidity()
    {
        QList<Venue*> result;
        for (auto &v : m_venues) result << &v;
        std::sort(result.begin(), result.end(),
            [](Venue *a, Venue *b) {
                return a->availableLiquidity > b->availableLiquidity;
            });
        return result;
    }

    struct RoutingDecision {
        Venue *venue;
        double quantity;
        double estimatedCost;
        double priority;
    };

    QList<Venue> m_venues;
};

4.2 延迟敏感的路由优化

cpp 复制代码
/**
 * 低延迟路由优化器
 * 使用Qt的信号槽机制和QTimer高精度定时器
 */
class LowLatencyRouter : public QObject
{
    Q_OBJECT

public:
    explicit LowLatencyRouter(QObject *parent = nullptr)
        : QObject(parent)
    {
        // 使用Qt::PreciseTimer实现高精度定时
        m_preciseTimer = new QTimer(this);
        m_preciseTimer->setTimerType(Qt::PreciseTimer);
        connect(m_preciseTimer, &QTimer::timeout,
                this, &LowLatencyRouter::onPreciseTick);
    }

    void routeWithMinLatency(const Order &order)
    {
        // 并行发送到所有场所,谁先成交算谁的
        // 使用Qt Concurrent实现并行下单
        QList<QFuture<void>> futures;
        for (const auto &venue : m_venues) {
            QFuture<void> f = QtConcurrent::run([this, order, venue]() {
                sendOrderToVenue(order, venue);
            });
            futures << f;
        }

        // 等待第一个成交确认(使用Qt信号槽)
        QEventLoop loop;
        QFutureWatcher<void> watcher;
        connect(&watcher, &QFutureWatcher<void>::finished,
                &loop, &QEventLoop::quit);
        watcher.setFuture(futures.first());
        loop.exec();
    }

private:
    void sendOrderToVenue(const Order &order, const Venue &venue)
    {
        // 记录发送时间(用于延迟分析)
        qint64 sendTime = QDateTime::currentMSecsSinceEpoch();

        // 通过券商API发送订单
        QString orderId = venue.api->sendOrder(order);

        // 等待成交确认(使用QTimer做超时管理)
        auto *timeoutTimer = new QTimer(this);
        timeoutTimer->setSingleShot(true);
        timeoutTimer->setInterval(5000);  // 5秒超时

        connect(timeoutTimer, &QTimer::timeout, this, [orderId, venue]() {
            qWarning() << "Order" << orderId
                       << "to venue" << venue.name << "timed out";
        });

        timeoutTimer->start();
    }

    QTimer *m_preciseTimer;
    QList<Venue> m_venues;
};

五、Implementation Shortfall(IS)算法

IS算法考虑市场冲击和时机风险,动态调整执行节奏:

cpp 复制代码
/**
 * Implementation Shortfall 拆单引擎
 * 目标:最小化冲击成本 + 时机风险
 */
class ISOrderSplitter : public QObject
{
    Q_OBJECT

public:
    struct Config {
        double riskAversion;     // 风险厌恶系数(0-1)
        double marketImpactFactor; // 市场冲击因子
        int timeHorizonMinutes;  // 执行时间范围
    };

    explicit ISOrderSplitter(const Config &config, QObject *parent = nullptr)
        : QObject(parent), m_config(config)
    {}

    QVector<double> calculateOptimalSchedule(double totalQty)
    {
        /**
         * 使用Almgren-Chriss模型计算最优执行轨迹
         *
         * 目标函数:
         *   min E[(X_T - Σp_i·q_i)²] + λ·Var[Σp_i·q_i]
         * 其中 λ = riskAversion
         *
         * 解析解(简化版):
         *   q_t = (2·(T-t) / T²) · Q_total   (线性执行)
         * 或更优的:
         *   q_t = (η/(2λσ²)) · (p_t - p_0)   (根据价格偏离动态调整)
         */

        int T = m_config.timeHorizonMinutes;
        QVector<double> schedule(T, 0.0);

        double eta = m_config.marketImpactFactor;   // 冲击系数
        double lambda = m_config.riskAversion;       // 风险厌恶
        double sigma = calculateVolatility();        // 波动率

        // Almgren-Chriss最优执行率
        double k = qSqrt(eta / (lambda * sigma * sigma));
        double c1 = (1.0 - qExp(-k * T)) / (1.0 + qExp(-k * T));
        double c2 = (1.0 + qExp(-k * T)) / (1.0 - qExp(-k * T));

        for (int t = 0; t < T; ++t) {
            double tau = T - t;
            double numerator = qExp(k * tau) - qExp(-k * tau);
            double denominator = qExp(k * T) - qExp(-k * T);
            double frac = numerator / denominator;

            schedule[t] = totalQty * frac;  // 第t分钟执行量
        }

        return schedule;
    }

private:
    double calculateVolatility() const
    {
        // 使用近期收益率计算波动率(标准差)
        QList<double> returns;
        for (int i = 1; i < m_priceHistory.size(); ++i) {
            double r = qLn(m_priceHistory[i] / m_priceHistory[i-1]);
            returns << r;
        }

        double mean = std::accumulate(returns.begin(), returns.end(), 0.0)
                     / returns.size();
        double variance = 0;
        for (double r : returns) {
            variance += (r - mean) * (r - mean);
        }

        return qSqrt(variance / returns.size());
    }

    Config m_config;
    QList<double> m_priceHistory;
};

六、性能优化:低延迟关键技术

6.1 使用内存池避免动态分配

cpp 复制代码
/**
 * 订单对象内存池
 * 避免高频交易场景下QObject创建/销毁的开销
 */
template<typename T>
class ObjectPool
{
public:
    explicit ObjectPool(int initialSize = 1024)
    {
        for (int i = 0; i < initialSize; ++i) {
            m_freeList.append(new T);
        }
    }

    T* acquire()
    {
        if (m_freeList.isEmpty()) {
            // 池已空,创建新的(应该很少发生)
            return new T;
        }
        return m_freeList.takeLast();
    }

    void release(T *obj)
    {
        obj->reset();  // 重置对象状态
        m_freeList.append(obj);
    }

    ~ObjectPool()
    {
        qDeleteAll(m_freeList);
    }

private:
    QList<T*> m_freeList;
};

6.2 使用无锁队列进行跨线程通信

cpp 复制代码
#include <QAtomicInt>
#include <QAtomicPointer>

/**
 * 无锁环形缓冲区(Lock-Free Ring Buffer)
 * 用于订单路由引擎的高频跨线程通信
 */
template<typename T, int Size>
class LockFreeRingBuffer
{
public:
    LockFreeRingBuffer() : m_head(0), m_tail(0) {}

    bool enqueue(const T &item)
    {
        int head = m_head.loadAcquire();
        int nextHead = (head + 1) % Size;

        if (nextHead == m_tail.loadAcquire()) {
            return false;  // 队列满
        }

        m_buffer[head] = item;
        m_head.storeRelease(nextHead);
        return true;
    }

    bool dequeue(T &item)
    {
        int tail = m_tail.loadAcquire();

        if (tail == m_head.loadAcquire()) {
            return false;  // 队列空
        }

        item = m_buffer[tail];
        m_tail.storeRelease((tail + 1) % Size);
        return true;
    }

private:
    T m_buffer[Size];
    QAtomicInt m_head;
    QAtomicInt m_tail;
};

6.3 使用Qt::DirectConnection减少信号槽开销

cpp 复制代码
// 在高频场景下,使用Qt::DirectConnection避免事件队列延迟
connect(marketDataFeed, &MarketDataFeed::tickArrived,
        this, &OrderRouter::onTickArrived,
        Qt::DirectConnection);  // 同步调用,无队列延迟

// 注意:DirectConnection要求槽函数在同一线程,
// 跨线程场景仍需使用QueuedConnection

七、完整实战:订单路由系统Demo

cpp 复制代码
#include <QApplication>
#include <QMainWindow>
#include <QWidget>
#include <QVBoxLayout>
#include <QHBoxLayout>
#include <QPushButton>
#include <QComboBox>
#include <QSpinBox>
#include <QDoubleSpinBox>
#include <QLabel>
#include <QTextEdit>
#include <QTimer>
#include <QDateTime>
#include <QRandomGenerator>
#include <QtConcurrent>
#include <QFutureWatcher>

// ============ 数据结构 ============

struct Order {
    enum Side { Buy, Sell } side;
    enum Type { Market, Limit } orderType;
    QString orderId;
    QString symbol;
    double quantity;
    double price;
    int sliceIndex = -1;
    qint64 timestamp;
};

struct ExecutionReport {
    QString orderId;
    QString execId;
    double filledQty;
    double filledPrice;
    qint64 timestamp;
    QString venueId;
};

// ============ 拆单引擎 ============

class OrderSplitterEngine : public QObject
{
    Q_OBJECT

public:
    explicit OrderSplitterEngine(QObject *parent = nullptr) : QObject(parent) {}

    void submitOrder(const Order &order, const QString &algorithm)
    {
        m_originalOrder = order;
        m_algorithm = algorithm;
        m_executedQty = 0;
        m_startTime = QDateTime::currentMSecsSinceEpoch();

        if (algorithm == "TWAP") {
            startTWAP();
        } else if (algorithm == "VWAP") {
            startVWAP();
        } else if (algorithm == "POV") {
            startPOV();
        }
    }

signals:
    void sliceGenerated(const Order &slice);
    void executionUpdate(double pct, double avgPrice);
    void executionComplete();

private:
    void startTWAP()
    {
        // 模拟:10个时间片,每个1秒
        int numSlices = 10;
        double qtyPerSlice = m_originalOrder.quantity / numSlices;

        auto *timer = new QTimer(this);
        connect(timer, &QTimer::timeout, this, [=, &qtyPerSlice]() mutable {
            static int sliceIdx = 0;
            if (sliceIdx >= numSlices) {
                timer->stop();
                emit executionComplete();
                return;
            }

            Order slice = m_originalOrder;
            slice.quantity = qtyPerSlice;
            slice.sliceIndex = sliceIdx;
            slice.timestamp = QDateTime::currentMSecsSinceEpoch();

            emit sliceGenerated(slice);

            m_executedQty += qtyPerSlice;
            sliceIdx++;

            emit executionUpdate(
                m_executedQty / m_originalOrder.quantity * 100.0,
                m_estimatedAvgPrice);
        });
        timer->start(1000);  // 每秒一个slice
    }

    void startVWAP()
    {
        // 简化:使用模拟成交量曲线
        QVector<double> volumeWeights = {0.08, 0.06, 0.07, 0.05, 0.06,
                                         0.08, 0.10, 0.12, 0.15, 0.23};
        double cumulative = 0;
        for (int i = 0; i < volumeWeights.size(); ++i) {
            QTimer::singleShot(i * 1000, this, [=]() {
                Order slice = m_originalOrder;
                slice.quantity = m_originalOrder.quantity * volumeWeights[i];
                slice.sliceIndex = i;
                slice.timestamp = QDateTime::currentMSecsSinceEpoch();
                emit sliceGenerated(slice);
            });
        }
    }

    void startPOV()
    {
        // 模拟:根据市场成交量动态调整
        auto *timer = new QTimer(this);
        connect(timer, &QTimer::timeout, this, [this]() {
            // 模拟市场成交量
            double marketVol = 1000 + QRandomGenerator::global()->bounded(500);
            double maxQty = marketVol * 0.10;  // 10% POV

            if (m_executedQty < m_originalOrder.quantity) {
                Order slice = m_originalOrder;
                slice.quantity = qMin(maxQty,
                    m_originalOrder.quantity - m_executedQty);
                slice.timestamp = QDateTime::currentMSecsSinceEpoch();
                emit sliceGenerated(slice);
                m_executedQty += slice.quantity;
            } else {
                timer->stop();
                emit executionComplete();
            }
        });
        timer->start(500);
    }

    Order m_originalOrder;
    QString m_algorithm;
    double m_executedQty = 0;
    double m_estimatedAvgPrice = 178.50;
    qint64 m_startTime;
};

// ============ 主窗口 ============

class OrderRoutingDemo : public QMainWindow
{
    Q_OBJECT

public:
    explicit OrderRoutingDemo(QWidget *parent = nullptr)
        : QMainWindow(parent)
    {
        setupUI();
        m_splitter = new OrderSplitterEngine(this);

        connect(m_splitter, &OrderSplitterEngine::sliceGenerated,
                this, &OrderRoutingDemo::onSliceGenerated);
        connect(m_splitter, &OrderSplitterEngine::executionUpdate,
                this, &OrderRoutingDemo::onExecutionUpdate);
        connect(m_splitter, &OrderSplitterEngine::executionComplete,
                this, [this]() {
                    m_log->append("✅ 执行完成!");
                });
    }

private:
    void setupUI()
    {
        auto *central = new QWidget(this);
        setCentralWidget(central);
        auto *mainLayout = new QVBoxLayout(central);

        // 控制面板
        auto *ctrlPanel = new QHBoxLayout;
        ctrlPanel->addWidget(new QLabel("标的:"));
        m_symbolCombo = new QComboBox;
        m_symbolCombo->addItems({"600519.SH", "000001.SZ", "AAPL.O", "TSLA.O"});
        ctrlPanel->addWidget(m_symbolCombo);

        ctrlPanel->addWidget(new QLabel("数量:"));
        m_qtySpin = new QDoubleSpinBox;
        m_qtySpin->setRange(100, 1000000);
        m_qtySpin->setValue(10000);
        m_qtySpin->setSingleStep(100);
        ctrlPanel->addWidget(m_qtySpin);

        ctrlPanel->addWidget(new QLabel("算法:"));
        m_algoCombo = new QComboBox;
        m_algoCombo->addItems({"TWAP", "VWAP", "POV"});
        ctrlPanel->addWidget(m_algoCombo);

        auto *startBtn = new QPushButton("开始拆单");
        connect(startBtn, &QPushButton::clicked,
                this, &OrderRoutingDemo::onStartSplit);
        ctrlPanel->addWidget(startBtn);

        mainLayout->addLayout(ctrlPanel);

        // 进度显示
        m_progressLabel = new QLabel("进度: 0%  |  预计均价: --");
        mainLayout->addWidget(m_progressLabel);

        // 日志
        m_log = new QTextEdit;
        m_log->setReadOnly(true);
        m_log->setFont(QFont("Consolas", 9));
        mainLayout->addWidget(new QLabel("执行日志:"));
        mainLayout->addWidget(m_log);

        setWindowTitle("Qt订单路由与拆单算法实战");
        resize(900, 600);
    }

private slots:
    void onStartSplit()
    {
        m_log->clear();
        m_log->append(QString("🚀 开始拆单: %1, 数量: %2, 算法: %3")
                      .arg(m_symbolCombo->currentText())
                      .arg(m_qtySpin->value())
                      .arg(m_algoCombo->currentText()));

        Order order;
        order.symbol = m_symbolCombo->currentText();
        order.side = Order::Buy;
        order.orderType = Order::Limit;
        order.quantity = m_qtySpin->value();
        order.price = 178.50;

        m_splitter->submitOrder(order, m_algoCombo->currentText());
    }

    void onSliceGenerated(const Order &slice)
    {
        QString msg = QString("[%1] Slice #%2: %3 × %4 @ %5")
                      .arg(QDateTime::fromMSecsSinceEpoch(slice.timestamp)
                           .toString("hh:mm:ss.zzz"))
                      .arg(slice.sliceIndex)
                      .arg(slice.quantity)
                      .arg(slice.symbol)
                      .arg(slice.price, 0, 'f', 2);
        m_log->append(msg);
    }

    void onExecutionUpdate(double pct, double avgPrice)
    {
        m_progressLabel->setText(
            QString("进度: %1%  |  预计均价: $%2")
            .arg(pct, 0, 'f', 1)
            .arg(avgPrice, 0, 'f', 2));
    }

private:
    QComboBox *m_symbolCombo;
    QDoubleSpinBox *m_qtySpin;
    QComboBox *m_algoCombo;
    QLabel *m_progressLabel;
    QTextEdit *m_log;
    OrderSplitterEngine *m_splitter;
};

int main(int argc, char *argv[])
{
    QApplication app(argc, argv);
    QApplication::setFont(QFont("Microsoft YaHei", 9));

    OrderRoutingDemo demo;
    demo.show();

    return app.exec();
}

#include "main.moc"

八、总结与算法选择指南

算法 适用场景 市场冲击 时机风险 实现复杂度
TWAP 流动性好的大盘股
VWAP 跟随市场节奏
POV 隐藏交易意图 最低
IS 平衡冲击与风险 低-中 低-中
手动拆解 小单/非敏感订单

核心原则:

  1. 大单必须拆,不拆必被盯
  2. 算法选择取决于流动性、波动率、时间敏感性
  3. 路由优化是毫秒级的竞争,必须精雕细琢
  4. 回测是验证拆单算法的唯一标准

《注:若有发现问题欢迎大家提出来纠正》

以上仅为技术分享参考,不构成投资建议

相关推荐
油炸自行车1 小时前
【bug】Qt 6 Q_NAMESPACE 跨 DLL 链接错误:LNK2019 无法解析 staticMetaObject
数据库·c++·qt·bug·link2019·q_namespace_exp·namespaceexport
阿正的梦工坊1 小时前
【Rust】20-Rust 编译器架构与 MIR/LLVM 优化管线
开发语言·架构·rust
在放️1 小时前
Python 爬虫 · XML、xpath 与 lxml 模块基础
开发语言·爬虫·python
我要打打代码1 小时前
C# 扩展方法
开发语言·c#
JackSparrow4141 小时前
彻底理解Java NIO(三)Java实现 I/O多路复用+Reactor模式及开源框架代码解读
java·c语言·开发语言·后端·nio·reactor模式
曹牧1 小时前
Java:Xml中的大、小于
java·开发语言
zavoryn1 小时前
Jackson 序列化踩坑:LocalDateTime、Long 精度丢失和 boolean isXxx 字段
java·开发语言·后端
曹牧1 小时前
Java:XML转义
xml·java·开发语言
leo_yu_yty1 小时前
Go语言分布式计算(并发Debug)
开发语言·笔记·后端·golang