From c6d253b2932dd5908dfd5fa61cb9c7c16c556450 Mon Sep 17 00:00:00 2001 From: "GDP\\solonot" Date: Tue, 4 Nov 2025 11:52:14 +0800 Subject: [PATCH] =?UTF-8?q?=E8=B0=83=E6=95=B4=E4=B8=8B=E5=8D=95=E9=80=BB?= =?UTF-8?q?=E8=BE=91=EF=BC=8C=E9=81=BF=E5=85=8D=E8=B6=85=E8=BF=87=E6=B6=A8?= =?UTF-8?q?=E8=B7=8C=E5=81=9C=E4=BB=B7=E4=BD=8D=E6=97=A0=E6=B3=95=E4=B8=8B?= =?UTF-8?q?=E5=8D=95=E7=9A=84=E6=83=85=E5=86=B5=E3=80=82?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- config.ini | 5 +- core/main_controller.py | 1 + core/sfgrid_trade_controller.py | 141 +++++++++++++++++++++----------- core/util.py | 7 +- example.db | Bin 12288 -> 12288 bytes sfgrid_constants.py | 8 +- starter.py | 1 - 7 files changed, 109 insertions(+), 54 deletions(-) diff --git a/config.ini b/config.ini index d3a58e3..aec6001 100644 --- a/config.ini +++ b/config.ini @@ -1,6 +1,9 @@ [config] miniQMTPath=D:\\Programs\\DTQMT\\userdata_mini +; miniQMTPath=D:\\Programs\\DTQMT_MN\\userdata_mini ; 测试账号 grid_price=1.665,1.660,1.655,1.650,1.645,1.640,1.635,1.630,1.625,1.620,1.615 -grid_volume = 100 +; grid_price=11.0,10.0,9.0,8.0,7.0,6.0,5.0,4.0,3.0,2.0,1.0 +grid_volume = 200 account_no = '99082560' +; account_no = '89009170' ; 测试账号 max_enabled_targets = 10 \ No newline at end of file diff --git a/core/main_controller.py b/core/main_controller.py index 27e12d9..a64d699 100644 --- a/core/main_controller.py +++ b/core/main_controller.py @@ -52,6 +52,7 @@ class SFGridController(XtQuantTraderCallback): self.seq = None print('- [成功]三疯交易系统初始化完成') + self.startMarketData() def startMarketData(self): diff --git a/core/sfgrid_trade_controller.py b/core/sfgrid_trade_controller.py index f40c46e..c7082a5 100644 --- a/core/sfgrid_trade_controller.py +++ b/core/sfgrid_trade_controller.py @@ -1,9 +1,12 @@ +from re import L +from core import util from core.strategy_db import TradeTarget from core.util import is_trading_time, queryPendingOrder from xtquant import xttrader, xtconstant from xtquant.xttype import StockAccount, XtOrder, XtOrderResponse, XtTrade import sfgrid_constants +import threading class StockTradeController: @@ -13,6 +16,7 @@ class StockTradeController: self.xt_trader: xttrader.XtQuantTrader = xt_trader self.account:StockAccount = account self.enabledTrading(enabled) + self.dataUpdateLock = threading.Lock() def getName(self): return "SFGRID" @@ -44,18 +48,68 @@ class StockTradeController: print(f' |- 仓位检查: 持仓需求充足, (gridVolume*gridIndex)={minRequirePosition}, 当前持仓:{self.tradeTarget.current_position}') else: print(f' |- 仓位检查: 持仓需求不足, (gridVolume*gridIndex)={minRequirePosition}, 当前持仓:{self.tradeTarget.current_position}') - if is_trading_time(): - self.two_way_order(True, True) def isEnabled(self) -> bool: return bool(self.tradeTarget.enabled) def onDataUpdate(self, data): - if self.isEnabled(): - print(f"\n标的{self.tradeTarget.stock_code}-{self.tradeTarget.stock_name} 行情数据更新 {data[self.tradeTarget.stock_code]}") + if not self.isEnabled(): + return + self.dataUpdateLock.acquire() + index = self.tradeTarget.grid_index + price = sfgrid_constants.grid_price[int(index)] # pyright: ignore[reportArgumentType] + lowPrice = sfgrid_constants.grid_price[int(index) + 1] # pyright: ignore[reportArgumentType] + highPrice = sfgrid_constants.grid_price[int(index) - 1] # pyright: ignore[reportArgumentType] + + lastPrice = float("{:.3f}".format(data[self.tradeTarget.stock_code]['lastPrice'])) + print(f"标的{self.tradeTarget.stock_code}-{self.tradeTarget.stock_name} 市价: {lastPrice}, 网格序号: {index}, 网格价格: {price}, 计划多单价: {lowPrice}, 计划空单价: {highPrice}") + + if lastPrice <= lowPrice: # 下下方多单 + orders = queryPendingOrder(str(self.tradeTarget.stock_code), self.getName(), self.xt_trader, self.account) + if len([order for order in orders if order.order_type == xtconstant.STOCK_BUY and order.price == lowPrice]) > 0: + # 已存在未交易的多单 + print(f' |- 已存在未交易的多单,不重复下单') + else: + print(f' |- 下网格多单') + self.tradeTarget.current_buy_order_no = self.xt_trader.order_stock_async( + self.account, + str(self.tradeTarget.stock_code), + xtconstant.STOCK_BUY, + sfgrid_constants.grid_volume, + xtconstant.FIX_PRICE, + lowPrice, + self.getName(), # strategy_name + self.tradeTarget.stock_code # remark # type: ignore + ) + self.tradeTarget.grid_index = int(index) + 1 # type: ignore + self.tradeTarget.current_buy_price = float(lowPrice) # type: ignore + print(f' |- 下网格多单号 {self.tradeTarget.current_buy_order_no}, 网格基准价 {price}, 下单价 {lowPrice}, 下单量 {sfgrid_constants.grid_volume}') + elif lastPrice == highPrice: # 下上方空单 + orders = queryPendingOrder(str(self.tradeTarget.stock_code), self.getName(), self.xt_trader, self.account) + if len([order for order in orders if order.order_type == xtconstant.STOCK_SELL and order.price == highPrice]) > 0: + # 已存在未交易的空单 + print(f' |- 已存在未交易的空单,不重复下单') + else: + print(f' |- 下网格空单') + self.tradeTarget.current_sell_order_no = self.xt_trader.order_stock_async( + self.account, + str(self.tradeTarget.stock_code), + xtconstant.STOCK_SELL, + sfgrid_constants.grid_volume, + xtconstant.FIX_PRICE, + highPrice, + self.getName(), + self.tradeTarget.stock_code) # type: ignore + self.tradeTarget.grid_index = int(index) - 1 # type: ignore + self.tradeTarget.current_sell_price = float(highPrice) # type: ignore + print(f' |- 下网格空单号 {self.tradeTarget.current_sell_order_no}, 网格基准价 {price}, 下单价 {highPrice}, 下单量 {sfgrid_constants.grid_volume}') + self.tradeTarget.save() + self.dataUpdateLock.release() + def onAsyncOrderResponse(self, order:XtOrderResponse): + self.dataUpdateLock.acquire() stockCode = order.order_remark orderSeq = order.seq if self.tradeTarget.current_buy_order_no == order.seq: @@ -65,12 +119,12 @@ class StockTradeController: else: print(f' |- 委托回调: 委托单 {order.order_id} {stockCode} {orderSeq} 不在策略监控范围内') return - rc = self.tradeTarget.save() print(f' |- 委托回调: 委托单 {order.order_id} {stockCode} {orderSeq} 处理结果: {rc}') + self.dataUpdateLock.release() def onOrderTrade(self, trade:XtTrade): - indicator = False + self.dataUpdateLock.acquire() if int(self.tradeTarget.status) == 0 and trade.order_id == self.initBuyOrderId : # type: ignore # 此时为建仓成交 self.tradeTarget.current_position = int(self.tradeTarget.current_position) + trade.traded_volume # 当前持仓数,账户原有持仓不在策略范围内 # type: ignore @@ -82,7 +136,6 @@ class StockTradeController: print(f' 成交价: {trade.traded_price} 成交量: {trade.traded_volume}') print(f' 当前持仓: {self.tradeTarget.current_position}') print(f' 网格坐标: {self.tradeTarget.grid_index}') - indicator = True # 双向下单 elif trade.order_id == self.tradeTarget.current_sell_order_no and int(self.tradeTarget.status) == 1: # type: ignore # 上涨一格:此时空单成交 self.tradeTarget.current_position = int(self.tradeTarget.current_position) - trade.traded_volume # type: ignore @@ -95,7 +148,6 @@ class StockTradeController: print(f' 网格坐标: {self.tradeTarget.grid_index}') cancelResult = self.xt_trader.cancel_order_stock_async(self.account, self.tradeTarget.current_buy_order_no) print(f' 上涨一格,空单成交,对侧买单已撤单 cancelResult: {cancelResult > 0}') - indicator = True # 双向下单 elif trade.order_id == self.tradeTarget.current_buy_order_no and int(self.tradeTarget.status) == 1: # type: ignore # 下跌一格:此时多单成交 self.tradeTarget.current_position = int(self.tradeTarget.current_position) + trade.traded_volume # type: ignore @@ -108,14 +160,11 @@ class StockTradeController: print(f' 网格坐标: {self.tradeTarget.grid_index}') cancelResult = self.xt_trader.cancel_order_stock_async(self.account, self.tradeTarget.current_sell_order_no) print(f' 下跌一格,多单成交,对侧卖单已撤单 cancelResult: {cancelResult > 0}') - indicator = True # 双向下单 else: # 打印订单信息和订单状态 print(f'|- 非策略内部订单,或订单状态不满足监控条件 {trade.order_id} {trade.stock_code}-{trade.instrument_name} {trade.commission}') - - if indicator and self.isEnabled() and is_trading_time(): - print(f'goto two way order') - self.two_way_order(True, True) # 双向下单 + + self.dataUpdateLock.release() # Description: 新标的,建基础仓 @@ -132,37 +181,37 @@ class StockTradeController: # Description: 网格跳格,双向下单 - def two_way_order(self, buy, sell): - print(f'|- 标的{self.tradeTarget.stock_code}-{self.tradeTarget.stock_name} 网格跳格,双向下单') - print(f' |- 网格坐标: {self.tradeTarget.grid_index}, buy:{buy}, sell:{sell}') - if buy and int(self.tradeTarget.grid_index)+1 < len(sfgrid_constants.grid_price): # 价格没有超过网格下边界,可以下多单 # type: ignore - currentPrice = sfgrid_constants.grid_price[int(self.tradeTarget.grid_index)] # type: ignore - buyPrice = sfgrid_constants.grid_price[int(self.tradeTarget.grid_index)+1] # type: ignore - self.tradeTarget.current_buy_order_no = self.xt_trader.order_stock_async( - self.account, - str(self.tradeTarget.stock_code), - xtconstant.STOCK_BUY, - sfgrid_constants.grid_volume, - xtconstant.FIX_PRICE, - buyPrice, - self.getName(), # strategy_name - self.tradeTarget.stock_code # remark # type: ignore - ) - self.tradeTarget.current_buy_price = float(buyPrice) # type: ignore - print(f' |- 下网格多单 OrderId {self.tradeTarget.current_buy_order_no}, 网格基准价 {currentPrice}, 下单价 {buyPrice}, 下单量 {sfgrid_constants.grid_volume}') + # def two_way_order(self, buy, sell): + # print(f'|- 标的{self.tradeTarget.stock_code}-{self.tradeTarget.stock_name} 网格跳格,双向下单') + # print(f' |- 网格坐标: {self.tradeTarget.grid_index}, buy:{buy}, sell:{sell}') + # if buy and int(self.tradeTarget.grid_index)+1 < len(sfgrid_constants.grid_price): # 价格没有超过网格下边界,可以下多单 # type: ignore + # currentPrice = sfgrid_constants.grid_price[int(self.tradeTarget.grid_index)] # type: ignore + # buyPrice = sfgrid_constants.grid_price[int(self.tradeTarget.grid_index)+1] # type: ignore + # self.tradeTarget.current_buy_order_no = self.xt_trader.order_stock_async( + # self.account, + # str(self.tradeTarget.stock_code), + # xtconstant.STOCK_BUY, + # sfgrid_constants.grid_volume, + # xtconstant.FIX_PRICE, + # buyPrice, + # self.getName(), # strategy_name + # self.tradeTarget.stock_code # remark # type: ignore + # ) + # self.tradeTarget.current_buy_price = float(buyPrice) # type: ignore + # print(f' |- 下网格多单 OrderId {self.tradeTarget.current_buy_order_no}, 网格基准价 {currentPrice}, 下单价 {buyPrice}, 下单量 {sfgrid_constants.grid_volume}') - if sell and int(self.tradeTarget.grid_index)-1 >=0: # 价格没有超过网格上边界,可以下空单 # type: ignore - currentPrice = sfgrid_constants.grid_price[int(self.tradeTarget.grid_index)] # type: ignore - sellPrice = sfgrid_constants.grid_price[int(self.tradeTarget.grid_index)-1] # type: ignore - self.tradeTarget.current_sell_order_no = self.xt_trader.order_stock_async( - self.account, - str(self.tradeTarget.stock_code), - xtconstant.STOCK_SELL, - sfgrid_constants.grid_volume, - xtconstant.FIX_PRICE, - sellPrice, - self.getName(), - self.tradeTarget.stock_code) # type: ignore - self.tradeTarget.current_sell_price = float(sellPrice) # type: ignore - print(f' |- 下网格空单 OrderId {self.tradeTarget.current_sell_order_no}, 网格基准价 {currentPrice}, 下单价 {sellPrice}, 下单量 {sfgrid_constants.grid_volume}') - self.tradeTarget.save() \ No newline at end of file + # if sell and int(self.tradeTarget.grid_index)-1 >=0: # 价格没有超过网格上边界,可以下空单 # type: ignore + # currentPrice = sfgrid_constants.grid_price[int(self.tradeTarget.grid_index)] # type: ignore + # sellPrice = sfgrid_constants.grid_price[int(self.tradeTarget.grid_index)-1] # type: ignore + # self.tradeTarget.current_sell_order_no = self.xt_trader.order_stock_async( + # self.account, + # str(self.tradeTarget.stock_code), + # xtconstant.STOCK_SELL, + # sfgrid_constants.grid_volume, + # xtconstant.FIX_PRICE, + # sellPrice, + # self.getName(), + # self.tradeTarget.stock_code) # type: ignore + # self.tradeTarget.current_sell_price = float(sellPrice) # type: ignore + # print(f' |- 下网格空单 OrderId {self.tradeTarget.current_sell_order_no}, 网格基准价 {currentPrice}, 下单价 {sellPrice}, 下单量 {sfgrid_constants.grid_volume}') + # self.tradeTarget.save() \ No newline at end of file diff --git a/core/util.py b/core/util.py index 0f576aa..2e2d848 100644 --- a/core/util.py +++ b/core/util.py @@ -1,7 +1,10 @@ +from typing import Any + + import sfgrid_constants import xtquant.xtconstant as xtconstant from xtquant import xtdata, xttrader -from xtquant.xttype import StockAccount, XtPosition +from xtquant.xttype import StockAccount, XtOrder, XtPosition import datetime def is_trading_time(): @@ -58,7 +61,7 @@ def getStockPosition(stock_code: str, xt_trader: xttrader.XtQuantTrader, account def minPosition(gridIndex:int): return sfgrid_constants.grid_volume * gridIndex -def queryPendingOrder(stock_code:str, tag: str, xt_trader: xttrader.XtQuantTrader, account: StockAccount): +def queryPendingOrder(stock_code:str, tag: str, xt_trader: xttrader.XtQuantTrader, account: StockAccount) -> list[XtOrder]: if stock_code == None or tag == None: return [] orders = xt_trader.query_stock_orders(account) diff --git a/example.db b/example.db index f9d6fcf2433ceab4355733057db5969c1bd368ed..5c2e124ad4734f2e6492c47248b8c692e2d6a2e8 100644 GIT binary patch delta 86 zcmZojXh@hK#l*gEqKp%e+?cS8pJgor|Ju!50;~BM*(V>6m)7SFVYUBdTd?>#mo1Do cG%~O>Ft9K*Gqe9CQuWY%od{gSbdiDp0NOSioB#j- delta 108 zcmZojXh@hK#l-x2qKp%e+?cS8pJh7(|MtyX0;~DGJ(=B^c^TwYIT$(E71So>^Q)0173UNlZ&vHw-|(0!dqwUL3LiLs%Hsi}eeuf^B7YzvmaMGO}y G2mk<0kRulW diff --git a/sfgrid_constants.py b/sfgrid_constants.py index e19434a..5135219 100644 --- a/sfgrid_constants.py +++ b/sfgrid_constants.py @@ -2,13 +2,13 @@ from typing import List import configparser # miniQMTPath = r'D:\\Programs\\DTQMT_MN\\userdata_mini' # miniQMT软件的安装路径 -# miniQMTPath = r'D:\\Programs\\DTQMT\\userdata_mini' # miniQMT软件的安装路径 +miniQMTPath = r'D:\\Programs\\DTQMT\\userdata_mini' # miniQMT软件的安装路径 # miniQMTPath = '' # grid_price = [11, 10, 9, 8, 7, 6, 5, 4, 3, 2, 1] # 网格价格设置,从高到低 grid_price:List[float] = [] # 网格价格设置,从高到低 grid_volume:int = 100 # 每个网格的交易手数 -# account_no:str = '99082560' -# account_no = '89009170' # 交易账号 +account_no:str = '99082560' +# account_no:str = '89009170' # 交易账号 max_enabled_targets:int = 10 def initConfig(): @@ -21,7 +21,7 @@ def initConfig(): grid_price = [float(item) for item in str_list] print(f'网格设置:{grid_price}') grid_volume = config.getint('config','grid_volume') - account_no = config.get('config','account_no') + # account_no = config.get('config','account_no') print(f'账号: {account_no}') max_enabled_targets = config.getint('config','max_enabled_targets') print(f'最大启用目标数: {max_enabled_targets}') diff --git a/starter.py b/starter.py index e4b2399..dce97c3 100644 --- a/starter.py +++ b/starter.py @@ -43,7 +43,6 @@ def pauseTrade(index:int): def stockTradeCtrl(index: int): return ctrl.stock_trade_ctrl[ctrl.instrument_pool[index].stock_code] - def help(): print("基础指令:") print(" ===================================================")