diff --git a/example.db b/example.db index 1d7a6ac..dc9205d 100644 Binary files a/example.db and b/example.db differ diff --git a/main_controller.py b/main_controller.py index ea15e6a..7efec97 100644 --- a/main_controller.py +++ b/main_controller.py @@ -1,12 +1,15 @@ # coding:utf-8 +from os import popen import time, sys + +from peewee import ModelSelect sys.stdout.reconfigure(encoding='utf-8') # 设置标准输出编码为UTF-8 import strategy_db import sfgrid_constants from sfgrid_trade_controller import StockTradeController from util import getInstrumentName, getStockPosition from xtquant.xttrader import XtQuantTrader -from xtquant.xttype import StockAccount, XtOrder +from xtquant.xttype import StockAccount, XtOrder, XtTrade from xtquant import xtdata from xtquant.xttrader import XtQuantTraderCallback import datetime @@ -51,7 +54,9 @@ class SFGridController(XtQuantTraderCallback): grid_index=0, last_trade_price=0.0, current_buy_price=0.0, - current_sell_price=0.0 + current_buy_order_no='', + current_sell_price=0.0, + current_sell_order_no='' ) new_target.save() print(f'新增交易标的 {stock_code} {stock_name}, {new_target.id}') @@ -59,13 +64,21 @@ class SFGridController(XtQuantTraderCallback): pos = getStockPosition(stock_code, self.xt_trader, self.account) strategy_db.TradeTarget.update(current_position=pos).where(strategy_db.TradeTarget.stock_code == stock_code).execute() # 更新标的池 - self.instrument_pool = strategy_db.TradeTarget.select() + self.refresh_targets() # 添加交易控制器 stockTradeController = StockTradeController(new_target, self.xt_trader, self.account, new_target.enabled) self.stock_trade_ctrl[stock_code] = stockTradeController except Exception as e: print(f'新增交易标的失败 {stock_code} {e}') + + + def del_trade_target(self, index:int): + target: strategy_db.TradeTarget = self.instrument_pool[index] + # self.stock_trade_ctrl. + del self.stock_trade_ctrl[target.stock_code] + target.delete_instance() + self.refresh_targets() def init_trader(self, path): session_id = int(time.time()) @@ -79,14 +92,20 @@ class SFGridController(XtQuantTraderCallback): self.account= StockAccount(account_id, account_type) print(f'- 交易账号对象初始化完成, 账号: {self.account.account_id}') + def init_instrument_pool(self): - self.instrument_pool = strategy_db.TradeTarget.select() + self.refresh_targets() for tradeTarget in self.instrument_pool: stockTradeController = StockTradeController(tradeTarget, self.xt_trader, self.account, tradeTarget.enabled) self.stock_trade_ctrl[tradeTarget.stock_code] = stockTradeController print(f'- 初始化标的池初始化完成 , 共 {len(self.instrument_pool)} 个标的') + + + def refresh_targets(self): + # 更新标的池 + self.instrument_pool:ModelSelect = strategy_db.TradeTarget.select() self.print_pool() def print_pool(self): @@ -170,19 +189,26 @@ class SFGridController(XtQuantTraderCallback): # ====== 市场回调方法 -- 以下方法由XtQuantData调用 ====== def onDataUpdate(self, data): # 遍历股池 + # for stock_code, data in data.items(): + # if data['lastPrice'] == 10.0: + # print(f'target = {stock_code} - {getInstrumentName(stock_code)} {data['lastPrice']}') + # self.add_trade_target(stock_code) + # self.stock_trade_ctrl[stock_code].enabledTrading(True) + + + # if stock_code not in self.stock_trade_ctrl: + # self.add_trade_target(stock_code) + # self.stock_trade_ctrl[stock_code].onDataUpdate(data) for target in self.instrument_pool: stock_code = target.stock_code # 如果存在对应的StockTradeController,则调用其onDataUpdate方法 - if stock_code not in self.stock_trade_ctrl: - print(f"股票代码 {stock_code} 未在交易控制器中找到,跳过处理。\n") - continue - if stock_code not in data: - print(f"股票代码 {stock_code} 未在行情数据中找到,跳过处理。\n") + if stock_code not in self.stock_trade_ctrl or stock_code not in data: + # print(f"股票代码 {stock_code} 未在交易控制器中找到,跳过处理。\n") continue stock_controller: StockTradeController = self.stock_trade_ctrl[stock_code] stock_controller.onDataUpdate(data) - + # ====== 市场回调方法 -- 以下方法由XtQuantTrader调用 ====== def on_connected(self): """ @@ -205,11 +231,12 @@ class SFGridController(XtQuantTraderCallback): """ stockCode = order.stock_code # 如果存在对应的StockTradeController,则调用其onDataUpdate方法 - if stockCode not in self.stock_trade_ctrl: - self.stock_trade_ctrl[stockCode].onOrderUpdate(order) + if stockCode in self.stock_trade_ctrl: + ctrl:StockTradeController = self.stock_trade_ctrl[stockCode] + ctrl.onOrderUpdate(order) print(datetime.datetime.now(), '委托回调 投资备注', order.order_remark) - def on_stock_trade(self, trade): + def on_stock_trade(self, trade:XtTrade): """ 成交变动推送 :param trade: XtTrade对象 diff --git a/sfgrid_constants.py b/sfgrid_constants.py index 25dc66f..a927196 100644 --- a/sfgrid_constants.py +++ b/sfgrid_constants.py @@ -1,4 +1,8 @@ -miniQMTPath = r'D:\\Programs\\DTQMT\\userdata_mini' # miniQMT软件的安装路径 -grid_price = [11, 10, 9, 8, 7, 6, 5, 4, 3, 2, 1] # 网格价格设置,从高到低 +miniQMTPath = r'D:\\Programs\\DTQMT_MN\\userdata_mini' # miniQMT软件的安装路径 +# D:\Programs\DTQMT_MN\userdata_mini +# grid_price = [11, 10, 9, 8, 7, 6, 5, 4, 3, 2, 1] # 网格价格设置,从高到低 +grid_price = [10.1, 10.00, 9.9, 9.8, 9.7, 9.6, 9.5, 9.4, 9.3, 9.2, 9.1] # 网格价格设置,从高到低 grid_volume = 100 # 每个网格的交易手数 -account_no = '99082560' # 交易账号 \ No newline at end of file +# account_no = '99082560' +account_no = '89009170' # 交易账号 +max_enabled_targets = 10 \ No newline at end of file diff --git a/sfgrid_trade_controller.py b/sfgrid_trade_controller.py index 71a7b54..a9f60fc 100644 --- a/sfgrid_trade_controller.py +++ b/sfgrid_trade_controller.py @@ -3,7 +3,7 @@ import strategy_db from util import getInstrumentName, getStockPosition from xtquant import xttrader, xtdata, xtconstant -from xtquant.xttype import StockAccount, XtOrder +from xtquant.xttype import StockAccount, XtOrder, XtTrade import sfgrid_constants @@ -14,7 +14,6 @@ class StockTradeController: self.xt_trader = xt_trader self.account = account self.currentPosition = getStockPosition(self.tradeTarget.stock_code, self.xt_trader, self.account) - self.tradeRecords = strategy_db.TradeRecord.select().where(strategy_db.TradeRecord.stock_code == self.tradeTarget.stock_code).order_by(strategy_db.TradeRecord.trade_time.desc()) def enabledTrading(self, enabled: bool): @@ -28,14 +27,7 @@ class StockTradeController: print(f"|- 标的{self.tradeTarget.stock_code}-{self.tradeTarget.stock_name} 建初始仓 买单准备中...\n") self.tradeTarget.grid_index = 1 self.tradeTarget.save() - self.initBuyOrderId = self.xt_trader.order_stock( - self.account, - self.tradeTarget.stock_code, - xtconstant.STOCK_BUY, - sfgrid_constants.grid_volume, - xtconstant.FIX_PRICE, - sfgrid_constants.grid_price[self.tradeTarget.grid_index], - 'sf_grid', f'{self.tradeTarget.stock_code}_init_buy') + self.init_stock_position() print(f"|- 标的{self.tradeTarget.stock_code}-{self.tradeTarget.stock_name} 建初始仓 买单已发出 InitBuyOrderId: {self.initBuyOrderId} Price: {sfgrid_constants.grid_price[self.tradeTarget.grid_index]} Volume: {sfgrid_constants.grid_volume}\n") else: print(f"|- 标的{self.tradeTarget.stock_code}-{self.tradeTarget.stock_name} 已有仓位或非初始状态 无需建初始仓 当前仓位: {self.tradeTarget.current_position} 状态: {self.tradeTarget.status}\n") @@ -46,21 +38,91 @@ class StockTradeController: def onDataUpdate(self, data): if self.isEnabled(): - print(f"标的{self.tradeTarget.stock_code}-{self.tradeTarget.stock_name} 行情数据更新 {data[self.tradeTarget.stock_code]}\n") + print(f"\n标的{self.tradeTarget.stock_code}-{self.tradeTarget.stock_name} 行情数据更新 {data[self.tradeTarget.stock_code]}") - def onOrderUpdate(self, order:XtOrder): - pass + def onOrderTrade(self, trade:XtTrade): + indicator = False + if trade.order_id == self.initBuyOrderId and self.tradeTarget.status == 0: + # 此时为建仓成交 + self.tradeTarget.current_position += sfgrid_constants.grid_volume # 当前持仓数,账户原有持仓不在策略范围内 + self.tradeTarget.last_trade_price = trade.traded_price + self.tradeTarget.grid_index = 1 + self.tradeTarget.status = 1 + self.tradeTarget.save() + print(f"|- 标的{self.tradeTarget.stock_code}-{self.tradeTarget.stock_name} 建初始仓订单ID: {self.initBuyOrderId}已成交 \n") + print(f' 成交价: {trade.traded_price} 成交量: {trade.traded_volume}\n') + print(f' 当前持仓: {self.tradeTarget.current_position}\n') + print(f' 网格坐标: {self.tradeTarget.grid_index}\n') + indicator = True # 双向下单 + elif trade.order_id == self.sellOrderId and self.tradeTarget.status == 1: + # 上涨一格:此时空单成交 + print(f"|- 标的{self.tradeTarget.stock_code}-{self.tradeTarget.stock_name} 上涨 卖单已成交 订单ID: {self.sellOrderId} Price: {sfgrid_constants.grid_price[self.tradeTarget.grid_index]} Volume: {sfgrid_constants.grid_volume} 手续费: {trade.commission}\n") + self.tradeTarget.current_position = -sfgrid_constants.grid_volume + self.tradeTarget.last_trade_price = trade.traded_price + self.tradeTarget.grid_index += 1 + self.tradeTarget.save() + print(f"|- 标的{self.tradeTarget.stock_code}-{self.tradeTarget.stock_name} 上涨 卖单已成交 订单ID: {self.sellOrderId} Price: {sfgrid_constants.grid_price[self.tradeTarget.grid_index]} Volume: {sfgrid_constants.grid_volume} 手续费: {trade.commission}\n") + print(f' 成交价: {trade.traded_price} 成交量: {trade.traded_volume}\n') + print(f' 当前持仓: {self.tradeTarget.current_position}\n') + print(f' 网格坐标: {self.tradeTarget.grid_index}\n') + indicator = True # 双向下单 + elif trade.order_id == self.buyOrderId and self.tradeTarget.status == 1: + # 下跌一格:此时多单成交 + self.tradeTarget.current_position = +sfgrid_constants.grid_volume + self.tradeTarget.last_trade_price = trade.traded_price + self.tradeTarget.grid_index -= 1 + self.tradeTarget.save() + print(f"|- 标的{self.tradeTarget.stock_code}-{self.tradeTarget.stock_name} 下跌 买单已成交 订单ID: {self.buyOrderId} Price: {sfgrid_constants.grid_price[self.tradeTarget.grid_index]} Volume: {sfgrid_constants.grid_volume} 手续费: {trade.commission}\n") + print(f' 成交价: {trade.traded_price} 成交量: {trade.traded_volume}\n') + print(f' 当前持仓: {self.tradeTarget.current_position}\n') + print(f' 网格坐标: {self.tradeTarget.grid_index}\n') + indicator = True # 双向下单 + else: + # 打印订单信息和订单状态 + print(f'非策略内部订单,或订单状态不满足监控条件 {trade.order_id} {trade.stock_code}-{trade.instrument_name} {trade.commission}') - # Description: 程序启动后 - def check_stock_position(self): - volume = getStockPosition(self.tradeTarget.stock_code, self.xt_trader, self.account) - pass + if indicator and self.isEnabled(): + self.two_way_order() # 双向下单 # Description: 新标的,建基础仓 def init_stock_position(self): - pass + self.initBuyOrderId = self.xt_trader.order_stock( + self.account, + self.tradeTarget.stock_code, + xtconstant.STOCK_BUY, + sfgrid_constants.grid_volume, + xtconstant.FIX_PRICE, + sfgrid_constants.grid_price[self.tradeTarget.grid_index], + 'sf_grid', f'{self.tradeTarget.stock_code}_init_buy') + print(f"|- 标的{self.tradeTarget.stock_code}-{self.tradeTarget.stock_name} 建初始仓 买单已发出 InitBuyOrderId: {self.initBuyOrderId} Price: {sfgrid_constants.grid_price[self.tradeTarget.grid_index]} Volume: {sfgrid_constants.grid_volume}\n") - # Description: 双向下单 + + # Description: 网格跳格,双向下单 def two_way_order(self): - pass \ No newline at end of file + if self.tradeTarget.grid_index+1 < len(sfgrid_constants.grid_price): # 价格没有超过网格下边界,可以下多单 + currentPrice = sfgrid_constants.grid_price[self.tradeTarget.grid_index] + buyPrice = sfgrid_constants.grid_price[self.tradeTarget.grid_index+1] + self.buyOrderId = self.xt_trader.order_stock( + self.account, + self.tradeTarget.stock_code, + xtconstant.STOCK_BUY, + sfgrid_constants.grid_volume, + xtconstant.FIX_PRICE, + buyPrice, + 'sf_grid', f'{self.tradeTarget.stock_code}_grid_down_{self.tradeTarget.grid_index}_{currentPrice}_{buyPrice}') + self.tradeTarget.current_buy_price = buyPrice + + if self.tradeTarget.grid_index-1 >=0: # 价格没有超过网格上边界,可以下空单 + currentPrice = sfgrid_constants.grid_price[self.tradeTarget.grid_index] + sellPrice = sfgrid_constants.grid_price[self.tradeTarget.grid_index-1] + self.sellOrderId = self.xt_trader.order_stock( + self.account, + self.tradeTarget.stock_code, + xtconstant.STOCK_SELL, + sfgrid_constants.grid_volume, + xtconstant.FIX_PRICE, + sellPrice, + 'sf_grid', f'{self.tradeTarget.stock_code}_grid_up_{self.tradeTarget.grid_index}_{currentPrice}_{sellPrice}') + self.tradeTarget.current_sell_price = sellPrice + self.tradeTarget.save() \ No newline at end of file diff --git a/starter.py b/starter.py index 690f400..61b5699 100644 --- a/starter.py +++ b/starter.py @@ -1,5 +1,7 @@ # coding:utf-8 import sys +import csv +import chardet import sfgrid_constants sys.stdout.reconfigure(encoding='utf-8') # 设置标准输出编码为UTF-8 @@ -18,12 +20,15 @@ def startMarketData(): def stopMarketData(): ctrl.stopMarketData() -def targets(): +def pool(): ctrl.print_pool() -def addTradeTarget(stock_code): +def addTarget(stock_code): ctrl.add_trade_target(stock_code) +def delTarget(index:int): + ctrl.del_trade_target(index) + def accountInfo(): ctrl.print_account_info() @@ -39,19 +44,31 @@ def pauseTrade(index:int): def stockTradeCtrl(index: int): return ctrl.stock_trade_ctrl[ctrl.instrument_pool[index].stock_code] +def importCsv(path:str): + with open(path, 'r', encoding='utf-8', errors='replace') as infile: + result = chardet.detect(infile) + print(result['encoding']) + # reader = csv.reader(infile) + # data = [row for row in reader] + # print(data) + + def help(): - print("可用命令:") + print("基础指令:") print(" ===================================================") print(" startMarketData() - 启动市场数据接收") - print(" stopMarketData() - 停止市场数据接收") - print(" addTradeTarget(stock_code) - 添加交易标的") + print(" stopMarketData() - 停止市场数据接收\n") + print(" pool() - 打印标的池信息") + print(" addTarget(stock_code) - 添加交易标的") + print(" delTarget(index) - 删除交易标的\n") print(" accountInfo() - 打印账户信息") - print(" positionInfo() - 打印持仓信息") - print(" targets() - 打印标的池信息") - print(" startTrade(index) - 启动标的交易线程") - print(" pauseTrade(index) - 暂停标的交易线程") - print(" stockTradeCtrl(index) - 获取标的交易控制器") + print(" positionInfo() - 打印持仓信息\n") + print(" startTrade(index) - 启动标的交易") + print(" pauseTrade(index) - 暂停标的交易") + print(" importCsv(path) - 导入CSV文件") print(" ===================================================") + print("内部指令:") + print(" stockTradeCtrl(index) - 获取标的交易控制器") print(" ctrl - 访问控制器实例") if __name__ == '__main__': diff --git a/strategy_db.py b/strategy_db.py index e2b0a27..4986488 100644 --- a/strategy_db.py +++ b/strategy_db.py @@ -16,13 +16,8 @@ class TradeTarget(BaseModel): grid_index = IntegerField() last_trade_price = FloatField() current_buy_price = FloatField() + current_buy_order_no = CharField(default='') current_sell_price = FloatField() - status = IntegerField(default=0) # 0表示新建,1表示已建初始仓 + current_sell_order_no = CharField(default='') + status = IntegerField(default=0) # 0表示新标的,1表示已建初始仓,正常交易中 enabled = BooleanField(default=False) # 是否启动交易线程 - -class TradeRecord(BaseModel): - stock_code = CharField() - trade_type = CharField() # 'buy' 或 'sell' - price = FloatField() - volume = IntegerField() - trade_time = CharField() # 可以存储为字符串格式的时间 \ No newline at end of file