本示例展示如何通过 `order_callback` 委托回报回调和 `get_trade_detail_data` 委托查询函数，实现委托状态跟踪和防止重复下单。策略在发送委托后设置待成交标志，后续 Bar 中先检查委托是否已结束，未结束则跳过下单，从而避免同一信号反复触发导致的重复委托问题。示例中的真实委托代码默认关闭。

!!! danger "高风险警告"
    本示例包含真实委托代码（`passorder`）。示例中的真实委托代码默认关闭，需将 `ENABLE_REAL_ORDER` 设为 `True` 才会发送委托。在仿真交易环境充分验证前，请勿在真实交易环境中启用。

## 示例目标

实现以下委托管理能力：

- 发送委托后记录委托号并设置待成交标志
- 通过 `order_callback` 回调实时获取委托号
- 通过 `get_trade_detail_data` 查询委托状态
- 在有待成交委托时阻止重复下单
- 委托进入终态后恢复下单权限

## 适用环境

| 环境 | 是否支持 |
|------|----------|
| 回测环境 | 不适用（回测无真实委托回报机制） |
| 模拟信号 | 不适用（模拟信号不产生委托） |
| 仿真交易环境 | 支持 |
| 真实交易环境 | 支持 |

## 使用周期

日线（`1d`）。

## 是否可能产生真实委托

是（默认关闭）。代码中包含 `passorder` 调用，但通过 `ENABLE_REAL_ORDER` 开关控制，默认值为 `False`（不发送委托）。设为 `True` 后将在仿真交易或真实交易环境中发送实际委托。

## 完整代码

```python
# ============================================================
# 真实委托开关：默认关闭
# 设为 True 后将在仿真交易或真实交易环境中发送委托
# 示例中的真实委托代码默认关闭
# ============================================================
ENABLE_REAL_ORDER = False

# 委托终态状态码
# 57 = 全部成交
# 58 = 已撤
# 59 = 部分撤
# 废单等其他终态码以当前客户端版本为准
ORDER_TERMINAL_STATUS = [57, 58, 59]

def init(ContextInfo):
    """策略初始化"""
    ContextInfo.set_universe(['600000.SH'])
    ContextInfo.order_pending = False    # 是否有待成交委托
    ContextInfo.last_order_id = None     # 上一次委托号
    print(f'策略初始化完成 | 真实委托开关: {ENABLE_REAL_ORDER}')

def handlebar(ContextInfo):
    """主策略逻辑"""
    if not ContextInfo.is_last_bar():
        return

    # ---- 步骤1：检查待成交委托状态 ----
    if ContextInfo.order_pending:
        if check_order_finished(ContextInfo):
            ContextInfo.order_pending = False
            print('待成交委托已结束，恢复下单权限')
        else:
            return  # 有未结束的委托，不重复下单

    # ---- 步骤2：获取行情与持仓 ----
    close = ContextInfo.get_close_price()
    if close is None or close <= 0:
        return

    pos = ContextInfo.get_position('600000.SH')
    has_position = pos and pos.m_nVolume > 0

    # ---- 步骤3：交易信号判断与下单 ----
    # 示例信号：无持仓时买入 100 股
    if not has_position:
        if ENABLE_REAL_ORDER:
            # 发送买入委托
            # quickTrade=2 表示同步等待下单结果
            passorder(0, 0, ContextInfo.accountid, '600000.SH', 0,
                      close, 100, 'state_track', 2, 0)
            ContextInfo.order_pending = True
            print(f'已发送买入委托: 100 股 @ {close:.2f}')
        else:
            print(f'[信号] 可买入 100 股 @ {close:.2f}（真实委托代码已关闭）')

def order_callback(ContextInfo, order_info):
    """委托回报回调：委托状态变化时触发，用于获取实际委托号"""
    ContextInfo.last_order_id = str(getattr(order_info, 'm_strOrderID', ''))
    status = getattr(order_info, 'm_nOrderStatus', 0)
    stock = getattr(order_info, 'm_strStockCode', '')
    direction = '买入' if getattr(order_info, 'm_nOrderType', 0) == 1 else '卖出'
    print(f'委托回报: {stock} {direction} 委托号={ContextInfo.last_order_id} 状态={status}')

    # 如果委托已进入终态，清除待成交标志
    if status in ORDER_TERMINAL_STATUS:
        ContextInfo.order_pending = False
        print(f'委托 {ContextInfo.last_order_id} 已结束（状态码: {status}）')

def check_order_finished(ContextInfo):
    """
    通过 get_trade_detail_data 查询委托状态
    返回 True 表示委托已结束，返回 False 表示仍未结束
    """
    orders = get_trade_detail_data(ContextInfo.accountid, 'ORDER', 'STOCK')
    if not orders:
        # 查不到任何委托记录，视为已结束
        return True

    target_id = str(ContextInfo.last_order_id) if ContextInfo.last_order_id else None

    for order in orders:
        order_id = str(getattr(order, 'm_strOrderID', ''))
        status = getattr(order, 'm_nOrderStatus', 0)

        if target_id is not None and order_id == target_id:
            if status in ORDER_TERMINAL_STATUS:
                print(f'查询确认: 委托 {order_id} 已结束 | 状态码: {status}')
                return True
            else:
                print(f'查询确认: 委托 {order_id} 未结束 | 状态码: {status}')
                return False

    # 未找到匹配的委托记录
    if target_id is not None:
        print(f'未找到委托 {target_id}，视为已结束')
    return True
```

## 代码说明（状态管理逻辑）

### 整体流程

```
handlebar 触发
    │
    ├── order_pending == True?
    │   ├── 是 → 查询委托状态 → 已结束? → 清除标志，继续
    │   │                       └─ 未结束? → return（跳过下单）
    │   └── 否 → 继续执行交易逻辑
    │
    ├── 获取行情与持仓
    │
    └── 信号触发?
        ├── ENABLE_REAL_ORDER == True → passorder 下单 → 设置 order_pending = True
        └── ENABLE_REAL_ORDER == False → 仅输出信号日志
```

### 状态变量

| 变量 | 类型 | 初始值 | 说明 |
|------|------|--------|------|
| `ENABLE_REAL_ORDER` | bool | `False` | 真实委托开关，默认关闭 |
| `ContextInfo.order_pending` | bool | `False` | 是否有待成交委托 |
| `ContextInfo.last_order_id` | str/None | `None` | 上一次委托号 |

### 委托号获取机制

```python
def order_callback(ContextInfo, order_info):
    ContextInfo.last_order_id = str(getattr(order_info, 'm_strOrderID', ''))
```

- `passorder` 发送委托后，`order_callback` 在委托状态变化时被触发。
- 通过 `order_info.m_strOrderID` 获取委托号并保存到 `ContextInfo.last_order_id`。
- 注意：`passorder` 的返回值与 `order_callback` 中的委托号可能不同步，以 `order_callback` 中的为准。

### 委托状态查询

```python
orders = get_trade_detail_data(ContextInfo.accountid, 'ORDER', 'STOCK')
```

| 参数 | 值 | 说明 |
|------|----|------|
| accountid | `ContextInfo.accountid` | 账户 ID |
| datatype | `'ORDER'` | 查询委托记录 |
| market | `'STOCK'` | 股票市场 |

`get_trade_detail_data` 返回委托对象列表，每个对象包含以下常用属性：

| 属性 | 说明 |
|------|------|
| `m_strOrderID` | 委托号 |
| `m_strStockCode` | 证券代码 |
| `m_nOrderType` | 委托类型（1=买入，2=卖出） |
| `m_nOrderStatus` | 委托状态码 |
| `m_nVolume` | 委托数量 |
| `m_dPrice` | 委托价格 |
| `m_nTradedVolume` | 已成交数量 |

### 委托状态码

| 状态码 | 说明 | 是否终态 |
|--------|------|----------|
| 50 | 委托已提交 | 否 |
| 55 | 委托已确认 | 否 |
| 56 | 部分成交 | 否 |
| 57 | 全部成交 | 是 |
| 58 | 已撤 | 是 |
| 59 | 部分撤 | 是 |

!!! note "提示"
    废单等其他终态状态码可能因客户端版本不同而异，以当前客户端版本为准。建议在 `ORDER_TERMINAL_STATUS` 列表中补充已知的废单状态码。

### 防重复下单机制

```python
if ContextInfo.order_pending:
    if check_order_finished(ContextInfo):
        ContextInfo.order_pending = False
    else:
        return  # 有未结束的委托，不重复下单
```

- 发送委托后立即设置 `order_pending = True`。
- 后续每次 `handlebar` 触发时，先检查是否有待成交委托。
- 如果委托未结束（非终态），直接 `return` 跳过下单逻辑。
- 如果委托已结束（终态），清除标志并继续执行交易逻辑。

### passorder 参数说明

```python
passorder(0, 0, ContextInfo.accountid, '600000.SH', 0,
          close, 100, 'state_track', 2, 0)
```

| 位置 | 参数 | 值 | 说明 |
|------|------|----|------|
| 1 | opType | 0 | 股票 |
| 2 | orderType | 0 | 买入 |
| 3 | accountid | ContextInfo.accountid | 账户 ID |
| 4 | orderCode | '600000.SH' | 证券代码 |
| 5 | prType | 0 | 价格类型 |
| 6 | price | close | 委托价格（最新收盘价） |
| 7 | volume | 100 | 委托数量 |
| 8 | strategyName | 'state_track' | 策略名称 |
| 9 | quickTrade | 2 | 同步等待下单结果 |
| 10 | userOrderId | 0 | 用户自定义委托号 |

!!! warning "quickTrade 参数"
    `quickTrade=2` 表示同步等待下单结果，会阻塞直到返回。如果对性能要求较高，可设为 `0`（异步）或 `1`（异步快速），但异步模式下委托号需通过 `order_callback` 获取。

## 安全注意事项

1. **真实委托开关默认关闭**：`ENABLE_REAL_ORDER` 默认为 `False`，不会发送任何委托。请先在仿真交易环境中验证逻辑正确后再设为 `True`。

2. **委托号获取时序**：`passorder` 发送委托后，`order_callback` 可能在下一个 `handlebar` 之前或之后触发。代码中通过 `check_order_finished` 函数查询委托状态作为兜底，即使回调未及时到达也能正确判断。

3. **状态码兼容性**：不同客户端版本的委托状态码可能存在差异。使用前请在仿真交易环境中验证状态码的含义，以当前客户端版本为准。

4. **异常处理**：生产环境中应在 `passorder` 调用和 `get_trade_detail_data` 查询周围添加 `try-except` 块，防止异常导致策略中断。

5. **资金与持仓检查**：下单前应检查可用资金是否充足、可卖持仓是否充足，本示例为简化演示未包含完整的前置检查。

## 真实委托代码默认关闭说明

本示例中所有 `passorder` 调用均受 `ENABLE_REAL_ORDER` 开关控制：

```python
ENABLE_REAL_ORDER = False  # 默认关闭

if ENABLE_REAL_ORDER:
    passorder(...)  # 仅当开关打开时执行
else:
    print('[信号] ...（真实委托代码已关闭）')
```

- **默认状态**：`False`，不发送委托，仅输出信号日志。
- **启用方式**：将 `ENABLE_REAL_ORDER` 改为 `True`。
- **启用前提**：已在仿真交易环境中充分验证委托状态跟踪逻辑正确无误。
- **启用风险**：设为 `True` 后将在对应环境中发送实际委托，仿真交易环境产生仿真委托，真实交易环境产生真实委托。

## 已知限制

- 本示例的交易信号仅为演示用途（无持仓即买入），不构成任何交易建议。
- `get_trade_detail_data` 查询的是当日委托记录，跨日的未成交委托可能无法查到。
- `order_callback` 的触发时机和频率取决于客户端版本和网络状况，以当前客户端版本为准。
- 本示例未实现撤单逻辑。如果委托长时间未成交，应考虑添加超时撤单机制。
- `passorder` 的 `prType=0` 在不同客户端版本中的行为可能略有差异，以当前客户端版本为准。
- 在真实交易环境中使用时，建议添加更完善的资金检查、涨跌停检查和异常处理逻辑。
