委托已报出但长时间未成交，通常是委托价格或数量与市场不匹配。最常见的原因包括：限价单价格偏离市场价、报价类型选择不当、委托触及涨跌停限制、委托数量不符合最小交易单位。通过检查委托价格与当前行情的匹配度，可以快速定位问题。

## 问题现象

策略成功提交了委托（委托记录中可以看到），但委托长时间处于"已报"或"部分成交"状态，没有全部成交。账户持仓未按预期变化。

## 直接判断

委托已报出说明 passorder 调用成功且被交易服务器接受。未成交的原因在于委托条件（价格、数量）与当前市场状况不匹配，或品种处于特殊状态（涨跌停、停牌）。

委托成交需要满足"价格匹配"和"数量匹配"两个条件。限价单只有在市场价格达到或优于委托价格时才会成交。如果委托价格远离市场价，委托会一直挂在订单簿上等待。市价单通常会立即成交，但在涨跌停或流动性不足时也可能无法成交。

<span class="docs-small-tip__label">小建议</span> 按以下 5 个步骤排查。重点是步骤 1（委托价格与行情对比）和步骤 2（报价类型），这两个是未成交的最常见原因。

## 排查步骤

### 步骤 1：检查委托价格与当前行情

限价单只有在市场价格达到委托价格时才会成交。买入委托的价格高于或等于当前卖一价时可以成交，卖出委托的价格低于或等于当前买一价时可以成交。

最常见的未成交原因是限价单价格设置不合理。例如，当前卖一价为 10.05 元，但买入限价设为 10.00 元，委托会挂在订单簿上等待价格回落到 10.00 元才成交。如果价格持续上涨，委托永远不会成交。同样，卖出限价设得过高也会导致无法成交。

**操作方法：** 在 QMT 客户端中查看该品种的当前买卖盘价格，与委托价格对比。在策略中获取实时行情数据进行比较。

**正确结果：** 买入委托价格不低于当前卖一价，或卖出委托价格不高于当前买一价，委托能够成交。

```python
def handlebar(ContextInfo):
    if not ContextInfo.is_last_bar():
        return
    
    stock = '000001.SZ'
    # 获取当前行情
    last_price = ContextInfo.get_last_price(stock)
    bid_price = ContextInfo.get_bid_price(stock)  # 买价列表
    ask_price = ContextInfo.get_ask_price(stock)  # 卖价列表
    
    print(f'{stock} 行情:')
    print(f'  最新价: {last_price}')
    if bid_price:
        print(f'  买一价: {bid_price[0]}')
    if ask_price:
        print(f'  卖一价: {ask_price[0]}')
    
    # 买入委托价格应接近卖一价才能快速成交
    if ask_price:
        buy_price = ask_price[0]  # 使用卖一价作为买入价
        print(f'  建议买入价: {buy_price}')
```

### 步骤 2：检查报价类型

passorder 的 priceType 参数决定报价方式。常用的价格类型包括：11（限价单）、12（最新价）、14（市价）、5（最优价）、0（最优五档即时成交剩撤销）。不同价格类型的成交逻辑不同。

限价单（priceType=11）需要指定精确价格，只有市场价格达到该价格才成交。市价单（priceType=14）以当前市场最优价格成交，通常能立即成交但价格不确定。如果希望快速成交，应使用市价单或以对手价作为限价单价格。使用限价单但价格设为 0 会导致委托无效。

**操作方法：** 检查 passorder 中的 priceType 参数。如果需要快速成交，考虑使用 priceType=14（市价）或 priceType=12（最新价）。如果使用限价单，确保 price 参数为合理的市场价格。

**正确结果：** 报价类型与策略需求匹配，委托能够按预期成交。

```python
def handlebar(ContextInfo):
    if not ContextInfo.is_last_bar():
        return
    close = ContextInfo.get_close_price()
    
    # 方式1：市价单（快速成交，价格不确定）
    ContextInfo.passorder(
        0, 1101, ContextInfo.accountid,
        '000001.SZ', 14, 0, 100,  # priceType=14, price=0
        1, ContextInfo
    )
    
    # 方式2：限价单（精确控制价格，需合理定价）
    # ContextInfo.passorder(
    #     0, 1101, ContextInfo.accountid,
    #     '000001.SZ', 11, close, 100,  # priceType=11, price=close
    #     1, ContextInfo
    # )
    
    # 方式3：最新价（跟随最新价成交）
    # ContextInfo.passorder(
    #     0, 1101, ContextInfo.accountid,
    #     '000001.SZ', 12, 0, 100,  # priceType=12
    #     1, ContextInfo
    # )
```

### 步骤 3：检查涨跌停限制

当品种触及涨停价或跌停价时，无法以超过涨跌停价的价格成交。涨停时买入委托可能无法成交（没有卖盘），跌停时卖出委托可能无法成交（没有买盘）。

涨跌停是交易所对品种价格波动的限制。涨停价 = 前收盘价 * (1 + 涨跌幅限制)，跌停价 = 前收盘价 * (1 - 涨跌幅限制)。A股普通股票的涨跌幅限制为 10%（ST 股票为 5%）。当品种涨停时，所有买入委托都排队等待，只有有人卖出才能成交；跌停时则相反。如果你的委托价格超出了涨跌停范围，委托会被直接拒绝。

**操作方法：** 查看品种当前是否处于涨停或跌停状态。在策略中计算涨跌停价并与委托价格比较。

**正确结果：** 委托价格在涨跌停范围内，品种不在涨跌停状态（或方向正确）。

```python
def handlebar(ContextInfo):
    if not ContextInfo.is_last_bar():
        return
    
    stock = '000001.SZ'
    last_price = ContextInfo.get_last_price(stock)
    closes = ContextInfo.get_history_data('close', 2, stock)
    
    if closes and len(closes) >= 2:
        prev_close = closes[-2]  # 昨日收盘价
        limit_up = round(prev_close * 1.1, 2)   # 涨停价
        limit_down = round(prev_close * 0.9, 2)  # 跌停价
        
        print(f'{stock} 涨跌停检查:')
        print(f'  昨收: {prev_close}, 涨停: {limit_up}, 跌停: {limit_down}')
        print(f'  最新价: {last_price}')
        
        if last_price >= limit_up:
            print('  状态: 已涨停，买入委托可能无法成交')
        elif last_price <= limit_down:
            print('  状态: 已跌停，卖出委托可能无法成交')
        else:
            print('  状态: 正常交易')
```

### 步骤 4：检查委托数量

A 股股票的最小交易单位为 100 股（1 手），买入委托数量必须是 100 的整数倍。卖出委托数量不足 100 股的可以零股卖出。不同品种的最小交易单位可能不同（如可转债为 10 张）。

如果委托数量不是 100 的整数倍（如 150 股），委托可能被交易所拒绝或只成交 100 股。此外，如果委托数量超过市场当前的挂单量，委托可能只能部分成交，剩余部分继续挂单等待。

**操作方法：** 检查 passorder 中的 volume 参数是否为正确的交易单位。在策略中添加数量校验逻辑。

**正确结果：** 委托数量符合最小交易单位要求，委托正常成交。

```python
def handlebar(ContextInfo):
    if not ContextInfo.is_last_bar():
        return
    close = ContextInfo.get_close_price()
    
    # 确保数量为 100 的整数倍
    desired_value = 10000  # 目标金额
    volume = int(desired_value / close / 100) * 100  # 向下取整到100的倍数
    if volume < 100:
        volume = 100  # 最少 100 股
    
    print(f'计算委托数量: {volume} 股 (金额: {volume * close})')
    
    ContextInfo.passorder(
        0, 1101, ContextInfo.accountid,
        '000001.SZ', 11, close, volume,
        1, ContextInfo
    )
```

### 步骤 5：查看委托状态

通过 order_callback 回调或 get_orders 方法可以查看委托的实时状态。委托状态码包括：50（已提交）、55（已确认）、56（部分成交）、57（全部成交）、58（已撤）、59（部分撤）。

委托长时间停留在 50 或 55 状态说明没有成交。56 状态说明部分成交但还有剩余。通过监控委托状态变化，可以判断委托是否需要撤单后重新下单。

**操作方法：** 在策略中定义 order_callback 回调监控委托状态。使用 get_orders 查询当日所有委托的状态。

**正确结果：** 委托状态最终变为 57（全部成交），或在超时后执行撤单操作。

```python
def handlebar(ContextInfo):
    if not ContextInfo.is_last_bar():
        return
    # 查询当日所有委托状态
    orders = ContextInfo.get_orders()
    for order in orders:
        status_text = {
            50: '已提交', 55: '已确认', 56: '部分成交',
            57: '全部成交', 58: '已撤', 59: '部分撤'
        }.get(order.m_nOrderStatus, '未知')
        print(f'委托: {order.m_strStockCode} {order.m_nVolume}股 状态:{status_text} 已成交:{order.m_nTradedVolume}')

def order_callback(ContextInfo, order_info):
    status = order_info.m_nOrderStatus
    print(f'委托回报: {order_info.m_strStockCode} 状态码:{status} 已成交:{order_info.m_nTradedVolume}/{order_info.m_nVolume}')
    if status == 56:
        print('  部分成交，剩余委托继续等待')
    elif status == 57:
        print('  全部成交')
```

## QMT 撤单方法

未成交的委托可以通过 cancel 方法撤销。可以撤销指定委托号的委托、撤销某品种的所有未成交委托、或撤销所有未成交委托。

撤单是将已提交但未成交的委托从交易所订单簿中移除。撤单后，委托状态变为 58（已撤）或 59（部分撤，如果有部分成交）。撤单是处理未成交委托的常用手段——撤单后可以重新以更合理的价格下单。

<span class="docs-small-tip__label">小建议</span> 在策略中设置超时撤单逻辑：委托提交后如果一定时间内未成交，自动撤单并重新定价下单。避免委托长时间挂在订单簿上占用资金或持仓。

```python
def init(ContextInfo):
    ContextInfo.pending_orders = {}  # 记录待成交委托

def handlebar(ContextInfo):
    if not ContextInfo.is_last_bar():
        return
    # 检查超时未成交委托
    from datetime import datetime, timedelta
    now = datetime.now()
    for order_id, info in list(ContextInfo.pending_orders.items()):
        submit_time = info.get('submit_time')
        if submit_time and (now - submit_time) > timedelta(minutes=5):
            # 超过 5 分钟未成交，撤单
            ContextInfo.cancel(order_id, ContextInfo)
            print(f'超时撤单: {order_id}')
            del ContextInfo.pending_orders[order_id]

def order_callback(ContextInfo, order_info):
    from datetime import datetime
    if order_info.m_nOrderStatus in [50, 55, 56]:
        # 委托未完成，记录到待成交列表
        ContextInfo.pending_orders[order_info.m_strOrderSysID] = {
            'submit_time': datetime.now(),
            'stock': order_info.m_strStockCode,
        }
    elif order_info.m_nOrderStatus in [57, 58, 59]:
        # 委托结束，从待成交列表移除
        order_id = order_info.m_strOrderSysID
        if order_id in ContextInfo.pending_orders:
            del ContextInfo.pending_orders[order_id]
```

## 最小验证代码

使用以下代码验证委托成交流程。此代码以市价单发送委托，应该能够立即成交：

```python
def init(ContextInfo):
    ContextInfo.set_universe(['000001.SZ'])
    ContextInfo.order_sent = False
    print('策略初始化完成')

def handlebar(ContextInfo):
    if not ContextInfo.is_last_bar():
        return
    if ContextInfo.order_sent:
        return
    
    last_price = ContextInfo.get_last_price('000001.SZ')
    if last_price and last_price > 0:
        print(f'当前价: {last_price}，发送市价买入委托')
        # 使用市价单，应该立即成交
        ContextInfo.passorder(
            0, 1101, ContextInfo.accountid,
            '000001.SZ', 14, 0, 100,
            1, ContextInfo
        )
        ContextInfo.order_sent = True

def deal_callback(ContextInfo, deal_info):
    print(f'成交回报: {deal_info.m_strStockCode}')
    print(f'  成交数量: {deal_info.m_nVolume}')
    print(f'  成交价格: {deal_info.m_dPrice}')

def order_callback(ContextInfo, order_info):
    print(f'委托回报: 状态码={order_info.m_nOrderStatus} 已成交={order_info.m_nTradedVolume}')
```

<span class="docs-small-tip__label">小建议</span> 如果市价单也无法成交，说明品种可能处于停牌、涨跌停或非交易时段。确认品种状态正常后再切换回限价单进行交易。对于限价单策略，建议添加超时撤单和重新定价逻辑，避免委托长时间挂单无效。
