new ui
This commit is contained in:
@@ -16,7 +16,7 @@ class SFGridTradeTarget(BaseModel):
|
||||
init_price = FloatField(null=True) # 建仓成本
|
||||
grid_match_count = IntegerField(default=0)
|
||||
grid_total_profit = FloatField(default=0.0)
|
||||
status = IntegerField(default=0) # -1表示新标的,未完成交易配置,0表示新标的,已完成交易配置,1表示已建初始仓,正常交易中
|
||||
status = IntegerField(default=0) # 已废弃,改用 strategy_type + grid_index
|
||||
enabled = BooleanField(default=False) # 是否启动交易线程
|
||||
strategy_type = IntegerField(default=0) # 0=未分类, 1=网格策略
|
||||
|
||||
|
||||
+58
-117
@@ -25,7 +25,7 @@ from core.sfgrid import bus_events
|
||||
from core.sfgrid.bus_events import EventTradeTargetUpdate
|
||||
import core.sfgrid.model as model
|
||||
from core.eventbus import event_bus
|
||||
from core.constants import OrderTypeBuy, OrderTypeSell, OrderTypeInit
|
||||
from core.constants import OrderTypeBuy, OrderTypeSell
|
||||
|
||||
from xtquant import xtconstant
|
||||
from xtquant.xttype import XtOrderError, XtOrderResponse, XtTrade
|
||||
@@ -65,8 +65,7 @@ class SFGridStrategy:
|
||||
|
||||
PrintLog(LogLevel.INFO,
|
||||
f'|- [DEBUG] 标的{tradeTarget.targetName()} 构造开始: '
|
||||
f'grid_index={tradeTarget.grid_index}, status={tradeTarget.status}, '
|
||||
f'enabled={tradeTarget.enabled}')
|
||||
f'网格={tradeTarget.grid_index}, 启用={tradeTarget.enabled}')
|
||||
|
||||
# orderGrid: 网格索引 → 订单编号(seq 或 order_id)的映射
|
||||
# seq 是 xtquant 返回的下单序号(下单瞬间),order_id 是交易所返回的正式订单号(异步回调后更新)
|
||||
@@ -149,30 +148,10 @@ class SFGridStrategy:
|
||||
# 获取当前该标的所有未成交订单
|
||||
orders = qmtv.queryPendingOrder(self.tradeTarget.stock_code, self.getName()) # type: ignore
|
||||
|
||||
# ── 分支1: status=0 未建仓 → 下建仓单 ──
|
||||
# 条件: 标的尚未建仓 且 不存在正在执行中的建仓单(防止重复建仓)
|
||||
init_remark = self._make_remark(OrderTypeInit, 1)
|
||||
if self.tradeTarget.status == 0 and not any(
|
||||
o.order_remark == init_remark for o in orders
|
||||
):
|
||||
# 建仓价取价格网格中最高价(grid_index=0 即列表第一个元素)
|
||||
price = self.tradeTarget.getPriceGrid()[0]
|
||||
tmpOrderSeq = qmtv.orderAsync(
|
||||
str(self.tradeTarget.stock_code),
|
||||
self.tradeTarget.grid_volume,
|
||||
xtconstant.STOCK_BUY, # 建仓 = 买入
|
||||
price,
|
||||
xtconstant.FIX_PRICE, # 限价单
|
||||
init_remark,
|
||||
self.getName(),
|
||||
)
|
||||
self.orderGrid[1] = tmpOrderSeq # 建仓单固定在网格索引 1
|
||||
PrintLog(LogLevel.INFO,
|
||||
f'|- 标的[{self.tradeTarget.targetName()}] 初始化: '
|
||||
f'建仓单,建仓价: {price:.3f}')
|
||||
|
||||
# ── 分支2: status=1 已建仓 → 下网格买卖单 ──
|
||||
elif self.tradeTarget.status == 1:
|
||||
# ── 统一网格逻辑 ──
|
||||
# grid_index=0 空仓: 只挂买单 @ grid[1],无持仓可卖
|
||||
# grid_index>0 有仓: 上方挂卖单 @ grid[idx-1],下方挂买单 @ grid[idx+1]
|
||||
if self.tradeTarget.grid_index >= 0:
|
||||
currentIdx = self.tradeTarget.grid_index # type: ignore
|
||||
|
||||
# --- 上方挂卖出单(空单)---
|
||||
@@ -272,19 +251,16 @@ class SFGridStrategy:
|
||||
启用或停用该标的的网格交易
|
||||
|
||||
启用时 (enabled=True):
|
||||
- status=0: 初始化网格索引后调用 refreshGridOrder 下建仓单
|
||||
- status=1: 检查持仓是否满足当前网格位置要求,满足则刷新网格订单
|
||||
不满足则回退 enabled=False(风控保护)
|
||||
- grid_index=0 空仓: 直接调用 refreshGridOrder(只挂买单)
|
||||
- grid_index>0 有仓: 检查持仓是否满足 grid_volume × grid_index
|
||||
满足则刷新网格单,不满足则回退 enabled=False(风控保护)
|
||||
|
||||
停用时 (enabled=False):
|
||||
- 取消该标的所有未成交订单,停止交易监控
|
||||
|
||||
返回:
|
||||
更新后的 tradeTarget 对象
|
||||
"""
|
||||
PrintLog(LogLevel.INFO,
|
||||
f" |- [DEBUG] enabledTrading({enabled}) 调用前: "
|
||||
f"grid_index={self.tradeTarget.grid_index}, status={self.tradeTarget.status}")
|
||||
f"grid_index={self.tradeTarget.grid_index}")
|
||||
|
||||
self.tradeTarget.enabled = enabled # type: ignore
|
||||
|
||||
@@ -294,38 +270,23 @@ class SFGridStrategy:
|
||||
f" |- 标的{self.tradeTarget.targetName()}交易启动, "
|
||||
f"持仓量:{self.tradeTarget.current_position}")
|
||||
|
||||
if self.tradeTarget.status == 0:
|
||||
# 未建仓状态: 初始化网格索引
|
||||
if self.tradeTarget.grid_index == 0:
|
||||
# grid_index=0 表示从未初始化过,设为 1(价格网格最高点建仓)
|
||||
self.tradeTarget.grid_index = 1 # pyright: ignore[reportAttributeAccessIssue]
|
||||
PrintLog(LogLevel.INFO,
|
||||
f" |- 标的{self.tradeTarget.targetName()}初始状态, "
|
||||
f"设置网格序号 1,")
|
||||
else:
|
||||
# grid_index 非零,保留之前设置的值(可能是手动修改的)
|
||||
PrintLog(LogLevel.INFO,
|
||||
f" |- 标的{self.tradeTarget.targetName()}初始状态, "
|
||||
f"保留网格序号 {self.tradeTarget.grid_index},")
|
||||
if self.tradeTarget.grid_index == 0:
|
||||
# 空仓: refreshGridOrder 会在 grid[1] 挂第一笔买单
|
||||
PrintLog(LogLevel.INFO,
|
||||
f" |- 标的{self.tradeTarget.targetName()}空仓, "
|
||||
f"等待首次买入建仓")
|
||||
else:
|
||||
# 已建仓状态: 检查现有持仓是否满足当前网格位置的仓位需求
|
||||
# 有仓: 检查现有持仓是否满足当前网格位置的仓位需求
|
||||
# 最小需求仓位 = 每格股数 × 当前网格索引
|
||||
# 例: grid_volume=100, grid_index=3 → 需持股 300 股
|
||||
PrintLog(LogLevel.INFO,
|
||||
f" |- 标的{self.tradeTarget.targetName()}已有仓位或非初始状态 "
|
||||
f"无需建初始仓 当前仓位: {self.tradeTarget.current_position} "
|
||||
f"状态: {self.tradeTarget.status}")
|
||||
|
||||
minRequirePosition: int = self.tradeTarget.grid_volume * int(self.tradeTarget.grid_index) # type: ignore
|
||||
|
||||
if minRequirePosition <= int(self.tradeTarget.current_position): # type: ignore
|
||||
# 持仓充足,可以继续网格交易
|
||||
PrintLog(LogLevel.INFO,
|
||||
f' |- 仓位检查: 持仓需求充足, '
|
||||
f'(gridVolume*gridIndex)={minRequirePosition}, '
|
||||
f'当前持仓:{self.tradeTarget.current_position}')
|
||||
else:
|
||||
# 持仓不足(可能是之前部分成交或手动减仓),风控:拒绝启用
|
||||
PrintLog(LogLevel.INFO,
|
||||
f' |- 仓位检查: 持仓需求不足, '
|
||||
f'(gridVolume*gridIndex)={minRequirePosition}, '
|
||||
@@ -333,7 +294,7 @@ class SFGridStrategy:
|
||||
f'交易启动失败')
|
||||
self.tradeTarget.enabled = False # type: ignore
|
||||
|
||||
# 无论 status=0 还是 status=1,最终都调用 refreshGridOrder 下对应的单
|
||||
# 刷新网格订单(空仓只挂买单,有仓买卖对冲)
|
||||
self.refreshGridOrder()
|
||||
|
||||
else:
|
||||
@@ -429,20 +390,17 @@ class SFGridStrategy:
|
||||
"""
|
||||
QMT 委托成交通知回调
|
||||
|
||||
成交后:
|
||||
1. 更新网格索引(卖出上移 / 买入下移)
|
||||
2. 如果是建仓单成交: status 0→1, 记录建仓价
|
||||
3. 如果是网格单成交: 累计网格匹配次数和总利润
|
||||
4. 从 orderGrid 删除已成交订单
|
||||
5. 持久化状态到数据库
|
||||
6. 调用 refreshGridOrder 挂新的网格单
|
||||
|
||||
trade.order_remark 格式: "{type},{gridIdx},{stockCode}"
|
||||
收到成交后:
|
||||
1. 判断成交方向(买入下移 / 卖出上移)→ 更新 grid_index
|
||||
2. 首次建仓(grid_index==0 时成交)→ 记录 init_price
|
||||
3. 卖出成交 → 累计 grid_match_count 和 grid_total_profit
|
||||
4. 清理 orderGrid → 持久化 → 刷新网格挂单
|
||||
"""
|
||||
# ── 过滤:只处理本策略本标的的成交 ──
|
||||
parsed = self._filter_event(trade.order_remark, trade.strategy_name)
|
||||
if parsed is None:
|
||||
return
|
||||
orderType, gridIdx, _ = parsed
|
||||
_, gridIdx, _ = parsed # gridIdx: 成交订单对应的网格索引(int)
|
||||
|
||||
PrintLog(LogLevel.INFO,
|
||||
f'|- 委托成交通知'
|
||||
@@ -451,61 +409,45 @@ class SFGridStrategy:
|
||||
|
||||
self.dataUpdateLock.acquire()
|
||||
try:
|
||||
desc: str = "" # 用于日志展示的成交类型描述
|
||||
|
||||
# ── 分支1: 建仓单成交 ──
|
||||
if orderType == OrderTypeInit:
|
||||
PrintLog(LogLevel.INFO,
|
||||
f'|- 委托成交通知[{self.tradeTarget.targetName()}-{trade.order_id}] '
|
||||
f'- 建仓单成交')
|
||||
# 状态切换: 未建仓(0) → 已建仓(1)
|
||||
self.tradeTarget.status = 1 # type: ignore
|
||||
# 记录建仓价格
|
||||
# ── 首次建仓:记录建仓价 ──
|
||||
# grid_index==0 表示成交前处于空仓状态,这笔成交就是首次建仓
|
||||
if self.tradeTarget.grid_index == 0:
|
||||
self.tradeTarget.init_price = trade.traded_price # type: ignore
|
||||
PrintLog(LogLevel.INFO,
|
||||
f'|- [DEBUG] 建仓单成交: '
|
||||
f'grid_index {self.tradeTarget.grid_index} → 1')
|
||||
# 建仓后网格索引固定为 1(价格网格最高点)
|
||||
self.tradeTarget.grid_index = 1 # type: ignore
|
||||
desc = "建仓单"
|
||||
|
||||
# ── 分支2: 网格单成交 ──
|
||||
# ── 网格方向判断 ──
|
||||
# 比较成交单的网格索引 vs 当前网格索引,判断价格移动方向
|
||||
oriIdx = self.tradeTarget.grid_index # 成交前的网格位置
|
||||
|
||||
if gridIdx > self.tradeTarget.grid_index:
|
||||
# 成交单在下方(更大索引 = 更低价格)→ 买入成交,持仓下移
|
||||
self.tradeTarget.grid_index += 1 # type: ignore
|
||||
# 首次建仓时 oriIdx==0,加上"建仓单"前缀便于识别
|
||||
desc = "建仓单(下移)" if oriIdx == 0 else "下移一格"
|
||||
|
||||
elif gridIdx < self.tradeTarget.grid_index:
|
||||
# 成交单在上方(更小索引 = 更高价格)→ 卖出成交,持仓上移
|
||||
self.tradeTarget.grid_index -= 1 # type: ignore
|
||||
# 卖出获利:累计匹配次数和利润
|
||||
self.tradeTarget.grid_match_count += 1 # type: ignore
|
||||
# 单格利润 = grid_size × 成交量
|
||||
self.tradeTarget.grid_total_profit += ( # type: ignore
|
||||
self.tradeTarget.grid_size * trade.traded_volume)
|
||||
desc = "上移一格"
|
||||
|
||||
else:
|
||||
PrintLog(LogLevel.INFO,
|
||||
f'|- 委托成交通知[{self.tradeTarget.targetName()}-{trade.order_id}] '
|
||||
f'- 网格单成交')
|
||||
oriIdx = self.tradeTarget.grid_index # 记录原网格位置(用于日志)
|
||||
# gridIdx == grid_index: 同格成交,正常情况下不会出现
|
||||
desc = "同格(异常)"
|
||||
|
||||
# 判断成交方向: gridIdx > currentIdx → 买入成交(下移)
|
||||
if gridIdx > self.tradeTarget.grid_index:
|
||||
desc = "下移一格"
|
||||
self.tradeTarget.grid_index += 1
|
||||
PrintLog(LogLevel.INFO,
|
||||
f'|- [{self.tradeTarget.targetName()}] '
|
||||
f'原网格 {oriIdx} → 现网格 {self.tradeTarget.grid_index}'
|
||||
f'({desc})')
|
||||
|
||||
# 判断成交方向: gridIdx < currentIdx → 卖出成交(上移)
|
||||
elif gridIdx < self.tradeTarget.grid_index:
|
||||
desc = "上移一格"
|
||||
# 累计统计
|
||||
self.tradeTarget.grid_match_count += 1 # 网格匹配次数+1
|
||||
self.tradeTarget.grid_total_profit += (
|
||||
self.tradeTarget.grid_size * trade.traded_volume
|
||||
) # 累计利润 = 网格间距 × 成交量
|
||||
self.tradeTarget.grid_index -= 1
|
||||
|
||||
# gridIdx == currentIdx: 理论上不应出现(同一个位置不会挂单给自己)
|
||||
else:
|
||||
desc = "保持格, 理论上不应该输出"
|
||||
|
||||
PrintLog(LogLevel.INFO,
|
||||
f'|- 委托成交通知'
|
||||
f'[{self.tradeTarget.stock_code}-{self.tradeTarget.stock_name} - '
|
||||
f'原网格位置 {oriIdx}, 现网格位置 {self.tradeTarget.grid_index}')
|
||||
|
||||
# ── 成交后处理 ──
|
||||
# 1. 持久化状态到数据库
|
||||
# ── 成交后统一处理 ──
|
||||
# 1. 持久化状态到数据库(grid_index、持仓量等已变更)
|
||||
self.saveProxy()
|
||||
# 2. 从 orderGrid 中删除已成交的订单(pop 防重复推送 KeyError)
|
||||
# 2. 从 orderGrid 清理已成交订单(pop 防 xtquant 重复推送 KeyError)
|
||||
self.orderGrid.pop(gridIdx, None)
|
||||
|
||||
# 3. 打印成交报告
|
||||
PrintLog(LogLevel.INFO,
|
||||
f"|- 成交报告[{self.tradeTarget.targetName()}] : "
|
||||
@@ -517,8 +459,7 @@ class SFGridStrategy:
|
||||
f' 成交价: {trade.traded_price} 成交量: {trade.traded_volume}')
|
||||
PrintLog(LogLevel.INFO,
|
||||
f' 手续费 : {trade.commission:.3f}')
|
||||
|
||||
# 4. 刷新网格订单:在新的 grid_index 上下重新挂买卖单
|
||||
# 4. 刷新网格订单:在新的 grid_index 位置重新挂买卖单
|
||||
self.refreshGridOrder()
|
||||
|
||||
finally:
|
||||
@@ -571,7 +512,7 @@ class SFGridStrategy:
|
||||
"""
|
||||
PrintLog(LogLevel.DEBUG,
|
||||
f'|- [DEBUG] saveProxy: {self.tradeTarget.targetName()} '
|
||||
f'grid_index={self.tradeTarget.grid_index}, status={self.tradeTarget.status}')
|
||||
f'网格={self.tradeTarget.grid_index}')
|
||||
rc = self.tradeTarget.save()
|
||||
event_bus.publish(EventTradeTargetUpdate, self.tradeTarget)
|
||||
return rc
|
||||
|
||||
File diff suppressed because it is too large
Load Diff
Reference in New Issue
Block a user