8.5 KiB
8.5 KiB
SFGrid 网格交易策略流程图
1. 总览:策略生命周期
flowchart TD
A["SFGridStrategy.__init__()"] --> B["订阅事件总线<br/>onOrderCreateAsync / onOrderTrade / onOrderError"]
B --> C["获取涨跌停价<br/>todayUpStopPrice / todayDownStopPrice"]
C --> D["loadExistOrders()<br/>从券商侧恢复未成交订单到 orderGrid"]
D --> E["enabledTrading(enabled)"]
E --> F{"enabled ?"}
F -->|True| G["启用交易流程 → 见 §3"]
F -->|False| H["停用交易流程 → 见 §3"]
G --> I["saveProxy() 持久化"]
H --> I
I --> J["构造完成,进入事件循环<br/>等待 QMT 回调 / UI 操作"]
2. 核心:refreshGridOrder() 网格下单
flowchart TD
START["refreshGridOrder()"] --> CHECK1{"qmtv.isMarketActive<br/>AND<br/>tradeTarget.enabled ?"}
CHECK1 -->|No| SKIP["跳过不下单"]
CHECK1 -->|Yes| QUERY["查询未成交订单<br/>queryPendingOrder()"]
QUERY --> STATUS{"tradeTarget.status ?"}
STATUS -->|"= 0 未建仓"| CHECK_INIT{"已存在建仓单?<br/>remark = 'INIT,1,{code}'"}
CHECK_INIT -->|"No 没有"| PLACE_INIT["下建仓单 (STOCK_BUY)<br/>价格 = getPriceGrid()[0]<br/>remark = 'INIT,1,{code}'"]
CHECK_INIT -->|"Yes 已有"| DONE_INIT["建仓单已在途,跳过"]
STATUS -->|"= 1 已建仓"| GET_IDX["currentIdx = grid_index"]
GET_IDX --> SELL_CHECK{"currentIdx > 0 ?<br/>(grid_index 不是最低点)"}
SELL_CHECK -->|"Yes 可挂卖单"| SELL_EXIST{"已存在同 remark 卖单?<br/>remark='SELL,{idx-1},{code}'"}
SELL_EXIST -->|"No 没有"| SELL_PLACE["下卖出单 (STOCK_SELL)<br/>价格 = grid[sellIdx]<br/>sellIdx = currentIdx - 1"]
SELL_EXIST -->|"Yes 已有"| SELL_SKIP["跳过,避免重复"]
SELL_CHECK -->|"No 价格已最低"| SELL_SKIP2["无卖出空间"]
SELL_PLACE --> BUY_CHECK
SELL_SKIP --> BUY_CHECK
SELL_SKIP2 --> BUY_CHECK
BUY_CHECK{"currentIdx < len(grid)-1 ?<br/>(grid_index 不是最高点)"}
BUY_CHECK -->|"Yes 可挂买单"| BUY_EXIST{"已存在同价同类型买单?<br/>order_type=BUY AND price=buyPrice"}
BUY_EXIST -->|"No 没有"| BUY_PLACE["下买入单 (STOCK_BUY)<br/>价格 = grid[buyIdx]<br/>buyIdx = currentIdx + 1"]
BUY_EXIST -->|"Yes 已有"| BUY_SKIP["跳过,避免重复"]
BUY_CHECK -->|"No 价格已最高"| BUY_SKIP2["无买入空间"]
3. 交易启停:enabledTrading()
flowchart TD
START["enabledTrading(enabled)"] --> SET["self.tradeTarget.enabled = enabled"]
SET --> BRANCH{"enabled ?"}
BRANCH -->|"True 启用"| STATUS{"tradeTarget.status ?"}
STATUS -->|"= 0 未建仓"| INIT_IDX{"grid_index == 0 ?"}
INIT_IDX -->|"Yes"| SET1["grid_index = 1<br/>(默认建仓位置)"]
INIT_IDX -->|"No"| KEEP["保留现有 grid_index"]
SET1 --> REFRESH1["refreshGridOrder()"]
KEEP --> REFRESH1
STATUS -->|"= 1 已建仓"| CALC["计算最小需求仓位<br/>min = grid_volume × grid_index"]
CALC --> CHECK{"current_position >= min ?"}
CHECK -->|"Yes 充足"| REFRESH2["refreshGridOrder()"]
CHECK -->|"No 不足"| DENY["拒绝启用<br/>enabled = False<br/>(风控保护)"]
BRANCH -->|"False 停用"| CANCEL["取消所有未成交订单<br/>cancel_order_stock_async()"]
CANCEL --> LOG["记录取消数量"]
REFRESH1 --> SAVE["saveProxy() 持久化"]
REFRESH2 --> SAVE
DENY --> SAVE
LOG --> SAVE
4. 事件回调链
flowchart TD
subgraph QMT["QMT / xtquant 层"]
OA["orderAsync()<br/>返回 seq"]
PUSH_ERR["C扩展推送<br/>XtOrderError"]
PUSH_RESP["C扩展推送<br/>XtOrderResponse"]
PUSH_TRADE["C扩展推送<br/>XtTrade"]
end
subgraph BUS["事件总线 event_bus"]
EVT_ERR["MarketOrderError"]
EVT_RESP["MarketOrderCreated"]
EVT_TRADE["MarketOrderTraded"]
end
subgraph STG["SFGridStrategy 回调"]
OE["onOrderError()"]
OC["onOrderCreateAsync()"]
OT["onOrderTrade()"]
end
OA --> PUSH_RESP
OA --> PUSH_ERR
PUSH_ERR --> EVT_ERR --> OE
PUSH_RESP --> EVT_RESP --> OC
PUSH_TRADE --> EVT_TRADE --> OT
5. onOrderError() 委托失败处理
flowchart TD
START["onOrderError(order_error)"] --> CHK1{"order_remark 非空 ?"}
CHK1 -->|"No 空"| EXIT1["无法解析,忽略"]
CHK1 -->|"Yes"| PARSE["解析 remark<br/>'{type},{gridIdx},{stockCode}'"]
PARSE --> CHK2{"len(parts) >= 3 ?"}
CHK2 -->|"No"| EXIT1
CHK2 -->|"Yes"| CHK3{"strategy_name == 'SFGRID'<br/>AND<br/>stockCode 匹配本标的 ?"}
CHK3 -->|"No 不匹配"| EXIT1
CHK3 -->|"Yes"| LOCK["获取 dataUpdateLock"]
LOCK --> DEL{"gridIdx in orderGrid ?"}
DEL -->|"Yes"| REMOVE["del orderGrid[gridIdx]<br/>清理孤立条目"]
DEL -->|"No"| LOG_ERR["记录错误日志<br/>error_id / error_msg"]
REMOVE --> LOG_ERR
LOG_ERR --> UNLOCK["释放 dataUpdateLock"]
6. onOrderCreateAsync() 订单确认
flowchart TD
START["onOrderCreateAsync(response)"] --> PARSE["解析 remark<br/>'{type},{gridIdx},{stockCode}'"]
PARSE --> FILTER{"strategy_name == 'SFGRID'<br/>AND len(parts) >= 3<br/>AND stockCode 匹配 ?"}
FILTER -->|"No"| EXIT["忽略"]
FILTER -->|"Yes"| LOCK["获取 dataUpdateLock"]
LOCK --> UPDATE["orderGrid[gridIdx] = response.order_id<br/>seq → order_id 替换"]
UPDATE --> UNLOCK["释放 dataUpdateLock"]
7. onOrderTrade() 成交处理
flowchart TD
START["onOrderTrade(trade)"] --> PARSE["解析 remark<br/>'{type},{gridIdx},{stockCode}'"]
PARSE --> FILTER{"strategy_name == 'SFGRID'<br/>AND len(parts) >= 3<br/>AND stockCode 匹配 ?"}
FILTER -->|"No"| EXIT["忽略"]
FILTER -->|"Yes"| LOCK["获取 dataUpdateLock"]
LOCK --> TYPE{"orderType ?"}
TYPE -->|"INIT 建仓单"| INIT["status = 1<br/>init_price = traded_price<br/>grid_index = 1"]
TYPE -->|"BUY / SELL 网格单"| CMP{"gridIdx vs grid_index ?"}
CMP -->|"gridIdx > grid_index<br/>(买入成交)"| DOWN["grid_index += 1<br/>下移一格"]
CMP -->|"gridIdx < grid_index<br/>(卖出成交)"| UP["grid_index -= 1<br/>上移一格<br/>match_count += 1<br/>total_profit += grid_size × volume"]
CMP -->|"gridIdx == grid_index<br/>(异常)"| SAME["日志: 理论上不应该输出"]
INIT --> POST
DOWN --> POST
UP --> POST
SAME --> POST
POST["成交后处理"] --> SAVE["saveProxy() 持久化状态"]
SAVE --> DEL["del orderGrid[gridIdx]<br/>移除已成交订单"]
DEL --> REPORT["打印成交报告<br/>成交价/量/手续费"]
REPORT --> REFRESH["refreshGridOrder()<br/>在新位置挂新的网格单"]
REFRESH --> UNLOCK["释放 dataUpdateLock"]
8. 网格交易完整状态机
stateDiagram-v2
[*] --> 未建仓: 创建 SFGridStrategy
未建仓 --> 建仓中: enabledTrading(True)<br/>下建仓单 INIT
建仓中 --> 已建仓: onOrderTrade(INIT)<br/>建仓单成交
建仓中 --> 建仓失败: onOrderError(INIT)<br/>委托被拒
建仓失败 --> 建仓中: refreshGridOrder()<br/>重新下建仓单
已建仓 --> 网格运行: refreshGridOrder()<br/>上下各挂一单
网格运行 --> 网格运行: onOrderTrade(SELL)<br/>卖出成交 → 上移<br/>重新挂单
网格运行 --> 网格运行: onOrderTrade(BUY)<br/>买入成交 → 下移<br/>重新挂单
网格运行 --> 单边挂单: onOrderError<br/>某方向委托失败
单边挂单 --> 网格运行: refreshGridOrder()<br/>重新补挂失败方向的单
已建仓 --> 已停用: enabledTrading(False)<br/>取消所有挂单
网格运行 --> 已停用: enabledTrading(False)
单边挂单 --> 已停用: enabledTrading(False)
已停用 --> 已建仓: enabledTrading(True)<br/>仓位检查通过
已停用 --> 已停用: enabledTrading(True)<br/>仓位不足,回退
9. 网格价格示意
价格
↑
│ grid[5] = 12.00 ← 最贵(顶部)
│ grid[4] = 11.50
│ grid[3] = 11.00 ← 当前位置 grid_index=3
│ grid[2] = 10.50 上方挂卖单 @10.50 (sellIdx=2, grid_index-1)
│ grid[1] = 10.00 下方挂买单 @10.00 (buyIdx=1, 已成交位置)
│ grid[0] = 9.50 ← 最便宜(底部/建仓价)
│
└──────────────────────→
grid_index=3 时:
卖单挂在 grid[2] @10.50 → 价格跌到 10.50 卖出(上移一格,赚差价)
买单挂在 grid[4] @11.50 → 价格涨到 11.50 买入(下移一格,补仓)
grid_size = grid[i] - grid[i-1] = 0.50(每格利润空间)