Skip to content

list_trades() occasionally returns a phantom "PendingSubmit" trade with empty ordno that never resolves and cannot be cancelled #216

Description

@meng-chiang

標題(建議)

list_trades() occasionally returns a phantom "PendingSubmit" trade with empty ordno that never resolves and cannot be cancelled

環境

  • shioaji 1.7.0
  • Python 3.12
  • 正式帳戶(simulation=False),期貨帳戶(futopt_account)
  • 商品:微型臺指期貨(TMF)

問題描述

list_trades() 偶爾會回傳一筆狀態為 PendingSubmit 的委託紀錄,具有以下特徵:

  1. ordno(交易所委託單號)欄位是空字串——正常委託(不論 pending 或已成交)這個欄位一律有值
  2. 在短時間內(約 1 秒內)反覆呼叫 update_status(account) + list_trades() 重新查詢,這筆紀錄完全沒有任何變化ordno 持續空白、deal_quantity/cancel_quantity 持續為 0、狀態不變)
  3. 對這筆委託呼叫 cancel_order(trade) 會失敗,回傳:
    StatusCode: 400, Detail: Please run update_status first to get trade details.
    
    即使呼叫 cancel_order() 之前已經呼叫過 update_status()
  4. 這筆委託在永豐的看盤 App/實際帳戶介面上完全不存在——使用者直接用 App 查詢委託明細,找不到對應的真實委託
  5. 這次發生時,前後數分鐘內沒有任何連線中斷/重連事件(已排除跟斷線重連相關)

由於我們的程式邏輯會把 list_trades() 回傳的 pending 委託口數計入部位計算(用來決定要補下多少口),這筆幽靈委託導致程式誤判「還有 1 口買單掛著」,因而多算了應該補償的賣出口數,造成實際交易口數跟預期不符,需要人工介入修正。

重現資料(正式環境真實 log,已去除帳號識別資訊)

幽靈委託(連續查詢 3 次,狀態完全凍結):

[broker] pending-check id=9338d51f ordno= action=Action.Buy qty=1 deal_qty=0 cancel_qty=0 status='PendingSubmit'
[broker] pending-check id=9338d51f ordno= action=Action.Buy qty=1 deal_qty=0 cancel_qty=0 status='PendingSubmit'
[broker] cancel_order error: StatusCode: 400, Detail: Please run update_status first to get trade details.
[broker] pending-check id=9338d51f ordno= action=Action.Buy qty=1 deal_qty=0 cancel_qty=0 status='PendingSubmit'

(三次查詢間隔約 1 秒,每次查詢前都呼叫過 update_status(account)

對照組:同一套查詢邏輯觀察到的正常委託(有 ordno、正常走完生命週期):

[broker] pending-check id=84a3a27c ordno=vn021 action=Action.Buy qty=1 deal_qty=0 cancel_qty=0 status='Submitted'
...(後續)...
[broker] pending-check id=84a3a27c ordno=vn021 action=Action.Buy qty=1 deal_qty=1 cancel_qty=0 status='Filled'

呼叫程式碼(Python,簡化版)

def _get_pending_tmf_trades(self):
    self._api.update_status(self._api.futopt_account)
    result = []
    for trade in self._api.list_trades():
        code = getattr(trade.contract, "code", "")
        if not code.startswith("TMF"):
            continue
        status = trade.status.status
        status_val = str(getattr(status, "value", status))
        if status_val not in {"PendingSubmit", "PreSubmitted", "Submitted", "PartFilled"}:
            continue
        result.append(trade)
    return result

想請教維護者的問題

  1. list_trades() 在什麼情況下會回傳一筆 status='PendingSubmit'ordno 是空字串的紀錄?是否為某種本地端佇列/快取殘留,而非交易所端真實存在的委託?
  2. cancel_order() 對這類紀錄回報「Please run update_status first」,是否就是 SDK 內部用來標示「這筆紀錄資料不完整/不是即時同步狀態」的預期行為?
  3. 有沒有建議的方式可以可靠地判別/過濾掉這類幽靈紀錄(例如是否可以信任「ordno 為空」這個判斷依據,還是有其他更準確的欄位)?
  4. 這個現象是否為已知問題,有沒有其他使用者回報過類似狀況?

補充:這次事故不是唯一一次

我們在 2026-07-29 也遇過類似情況(同一套邏輯偵測到一筆來源不明、口數為 +3 的 pending 委託,當時尚未加上詳細診斷 log,無法確認 ordno 是否同樣為空,但行為模式一致:刪不掉、使用者帳戶介面查無此委託)。這次(2026-08-12)是第一次能明確記錄到 ordno 為空這個特徵。

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Type

    No type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions