├── GUI.py ├── LICENSE ├── README.md └── trade.py /GUI.py: -------------------------------------------------------------------------------- 1 | import time 2 | import tkinter 3 | from tkinter import ttk 4 | from tkinter.constants import END, W, E 5 | 6 | import pandas 7 | 8 | 9 | class GUI: 10 | def __init__(self,info_queue,handle_queue): 11 | self.info_queue = info_queue 12 | self.handle_queue = handle_queue 13 | 14 | def gui(self): 15 | 16 | self.top_window = tkinter.Tk() 17 | self.top_window.title('LeekControler') 18 | ''' 19 | 创建菜单 20 | ''' 21 | menu = tkinter.Menu(self.top_window) 22 | menu.add_command(label='查看/修改参数',command=self.create_view_change_parm_window) 23 | self.top_window.config(menu=menu) 24 | 25 | # self.tree = ttk.Treeview(self.top_window, columns=('参数名', '值'), show="headings", 26 | # displaycolumns="#all",height=25) 27 | # self.tree.heading('参数名', text="参数名", anchor=W) 28 | # self.tree.heading('值', text="值", anchor=W) 29 | # 30 | # self.tree.pack() 31 | # tkinter.Button(self.top_window, text="修改参数值", command=self.create_parm_change_window).pack() 32 | # self.load_parm_data() 33 | self.top_window.mainloop() 34 | 35 | def create_view_change_parm_window(self): 36 | top = tkinter.Toplevel() 37 | top.title("查看参数") 38 | 39 | tree = ttk.Treeview(top, columns=('参数名', '值'), show="headings", 40 | displaycolumns="#all", height=25) 41 | tree.heading('参数名', text="参数名", anchor=W) 42 | tree.heading('值', text="值", anchor=W) 43 | 44 | tree.pack() 45 | tkinter.Button(top, text="修改参数值", command=self.create_parm_change_window).pack() 46 | self.load_parm_data(top,tree) 47 | 48 | def create_parm_change_window(self): 49 | top = tkinter.Toplevel() 50 | top.title("修改参数值") 51 | 52 | # 设置标签信息 53 | label1 = tkinter.Label(top, text='参数名:') 54 | label1.grid(row=0, column=0) 55 | label2 = tkinter.Label(top, text='参数值:') 56 | label2.grid(row=1, column=0) 57 | 58 | # 创建输入框 59 | entry1 = tkinter.Entry(top) 60 | entry1.grid(row=0, column=1, padx=10, pady=5) 61 | entry2 = tkinter.Entry(top) 62 | entry2.grid(row=1, column=1, padx=10, pady=5) 63 | 64 | button1 = tkinter.Button(top, text='修改', command=lambda:self.send_handle_queue(entry1.get(), entry2.get())).grid(row=3, column=0, 65 | sticky=W, padx=30, pady=5) 66 | button2 = tkinter.Button(top, text='退出', command=top.destroy).grid(row=3, column=1, 67 | sticky=E, padx=30, pady=5) 68 | def send_handle_queue(self,parm_name,value): 69 | self.handle_queue.put({'parm_name':parm_name,'value':float(value)}) 70 | return self 71 | 72 | def load_parm_data(self,top,tree): 73 | if(self.info_queue.empty()==False): 74 | parm_info_dict = self.info_queue.get() 75 | for item in tree.get_children(): 76 | tree.delete(item) 77 | for key, value in parm_info_dict.items(): 78 | tree.insert("", END, values=[key,value]) 79 | tree.pack() 80 | top.after(1, self.load_parm_data,top,tree) 81 | 82 | return self 83 | # def -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) 2023 QSimons 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. 22 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # miniQMT_trade 2 | # miniQMT_trade 1.0.0 3 | ## 1.介绍 4 | * miniQMT_trade是QSimons独立编写的miniQMT可转债量化交易系统,已于可转债实盘验证通过 5 | * 实现订单生命管理,可跟踪多个不同标的,高效处理回调数据 6 | * 可自定义止损/买卖挂单位置 7 | * 可自定义执行挂单未成交的撤单等操作 8 | * 支持GUI调参 9 | * 本项目仅用于python学习交流,代码示例,请勿用于投资等商业用途,如使用本代码进行交易,一切后果自负。 10 | 11 | 12 | 13 | ## 2.项目文件: 14 | 1.trade.py:核心文件,需要自行搭配交易信号发生的代码使用 15 | 16 | 2.GUI.py:辅助文件,可以在运行过程中读取交易系统的参数并调整交易系统的参数 17 | 18 | ## 3.维护 19 | @QSimons 20 | ## 4.更新记录 21 | 1.0.0---上传所有本地代码 22 | ## 5.未来更新 23 | 1.待定 24 | -------------------------------------------------------------------------------- /trade.py: -------------------------------------------------------------------------------- 1 | 2 | import os 3 | import pickle 4 | import random 5 | import time 6 | from multiprocessing import Queue, Process 7 | from typing import Iterable 8 | from xtquant.xttrader import XtQuantTraderCallback 9 | 10 | import matplotlib.pyplot as plt 11 | import datetime 12 | import akshare 13 | import numpy 14 | import numpy as np 15 | import pandas 16 | import seaborn as seaborn 17 | import xtquant 18 | from xtquant import xtconstant 19 | from xtquant.xttrader import XtQuantTrader, XtQuantTraderCallback 20 | from xtquant.xttype import StockAccount 21 | import statsmodels 22 | from pandas import Series 23 | from statsmodels.tsa.stattools import coint, adfuller 24 | import statsmodels.api 25 | 26 | from miniQMT_trade.GUI import GUI 27 | 28 | ''' 29 | 定义常数----------------------------------------------------- 30 | ''' 31 | BUY_ORDER_TBD = 11 32 | #待报订单,程序发出指令后但回报函数未回报状态 33 | BUY_ORDER_PD = 12 34 | #已报订单,程序发出指令后回报函数已经回报 35 | BUY_ORDER_CC_TBD = 21 36 | #待撤订单,程序发出指令后回报函数未回报状态 37 | SELL_ORDER_TBD = 31 38 | #待报订单,程序发出指令后但回报函数未回报状态 39 | SELL_ORDER_PD = 32 40 | #已报订单,程序发出指令后回报函数已经回报 41 | SELL_ORDER_CC_TBD = 41 42 | #待撤订单,程序发出指令后回报函数未回报状态 43 | END = 51 44 | #已经卖出,结束 45 | POS = 61 46 | #正在持仓,无其他指令 47 | 48 | ''' 49 | 定义常数----------------------------------------------------- 50 | ''' 51 | 52 | 53 | 54 | 55 | class Trade(XtQuantTraderCallback): 56 | def __init__(self,order_live_max_num=1,sleep_time=2.8,order_amount=10,stragety_name='harvest',max_epoach=4,max_buy_epoach=8,max_sell_epoach=3,penny_jump=0.21,penny_jump_mode=True,move_cut_loss_value=0.45): 57 | self.order_live_max_num = order_live_max_num 58 | 59 | self.order_live_dict = {} 60 | #dict结构:code:{order_status:(),buy_order_id:(),sell_order_id:(),is_responded:True/False} 61 | self.sleep_time = sleep_time 62 | 63 | self.order_amount = order_amount 64 | self.stragety_name = stragety_name 65 | 66 | self.max_epoach = max_epoach 67 | self.max_buy_epoach = max_buy_epoach 68 | self.max_sell_epoach = max_sell_epoach 69 | self.penny_jump = penny_jump 70 | self.penny_jump_mode = penny_jump_mode 71 | self.move_cut_loss_value = move_cut_loss_value 72 | def initial_trade(self): 73 | self.path = 'D:\\国金证券QMT交易端\\userdata_mini' 74 | self.session_id = random.randint(100000, 999999) 75 | self.xt_trader = XtQuantTrader(self.path, self.session_id) 76 | 77 | # self.xt_trader.set_relaxed_response_order_enabled(True) 78 | self.acc = StockAccount('') 79 | 80 | callback = self 81 | self.xt_trader.register_callback(callback) 82 | # 启动交易线程 83 | self.xt_trader.start() 84 | # 建立交易连接,返回0表示连接成功 85 | connect_result = self.xt_trader.connect() 86 | if connect_result != 0: 87 | import sys 88 | sys.exit('链接失败,程序即将退出 %d' % connect_result) 89 | subscribe_result = self.xt_trader.subscribe(self.acc) 90 | if subscribe_result != 0: 91 | print('账号订阅失败 %d' % subscribe_result) 92 | ''' 93 | 回调类函数------------------------------------------------------------------------------------------------------- 94 | ''' 95 | 96 | def on_disconnected(self): 97 | """ 98 | 连接断开 99 | :return: 100 | """ 101 | print("connection lost") 102 | 103 | def on_stock_order(self, order): 104 | """ 105 | 委托回报推送 106 | :param order: XtOrder对象 107 | :return: 108 | """ 109 | print("我是委托回报推送") 110 | print(order.stock_code, order.order_status, order.order_sysid) 111 | 112 | def on_stock_asset(self, asset): 113 | """ 114 | 资金变动推送 注意,该回调函数目前不生效 115 | :param asset: XtAsset对象 116 | :return: 117 | """ 118 | print("on asset callback") 119 | print(asset.account_id, asset.cash, asset.total_asset) 120 | 121 | def on_stock_trade(self, trade): 122 | """ 123 | 成交变动推送 124 | :param trade: XtTrade对象 125 | :return: 126 | """ 127 | print("已经成交!!!") 128 | self.handle_on_stock_trade(trade) 129 | print(trade.account_id, trade.stock_code, trade.order_id) 130 | 131 | def on_stock_position(self, position): 132 | """ 133 | 持仓变动推送 注意,该回调函数目前不生效 134 | :param position: XtPosition对象 135 | :return: 136 | """ 137 | print("on position callback") 138 | print(position.stock_code, position.volume) 139 | 140 | def on_order_error(self, order_error): 141 | """ 142 | 委托失败推送 143 | :param order_error:XtOrderError 对象 144 | :return: 145 | """ 146 | print("on order_error callback") 147 | print(order_error.order_id, order_error.error_id, order_error.error_msg) 148 | 149 | def on_cancel_error(self, cancel_error): 150 | """ 151 | 撤单失败推送 152 | :param cancel_error: XtCancelError 对象 153 | :return: 154 | """ 155 | print("on cancel_error callback") 156 | print(cancel_error.order_id, cancel_error.error_id, cancel_error.error_msg) 157 | 158 | def on_order_stock_async_response(self, response): 159 | """ 160 | 异步下单回报推送 161 | :param response: XtOrderResponse 对象 162 | :return: 163 | """ 164 | print("已经下单!!!!") 165 | 166 | print(response.account_id) 167 | self.handle_order_async_response(response) 168 | 169 | print(response.account_id, response.order_id, response.seq, response.order_remark) 170 | 171 | def on_account_status(self, status): 172 | """ 173 | :param response: XtAccountStatus 对象 174 | :return: 175 | """ 176 | print("on_account_status") 177 | print(status.account_id, status.account_type, status.status) 178 | 179 | def on_cancel_order_stock_async_response(self, response): 180 | """ 181 | :param response: XtCancelOrderResponse 对象 182 | :return: 183 | """ 184 | self.handle_cancel_respond(response) 185 | print('异步撤单回报') 186 | 187 | def handle_order_async_response(self, response): 188 | ''' 189 | 异步下单回报推送处理 190 | :return: 191 | ''' 192 | order_remark = response.order_remark 193 | side = order_remark[:1] 194 | code = order_remark[1:] 195 | if (side == 'b'): 196 | if (self.order_live_dict[code]['order_status'] == BUY_ORDER_TBD): 197 | self.order_live_dict[code]['order_status'] = BUY_ORDER_PD 198 | self.order_live_dict[code].update({'buy_order_id': response.order_id}) 199 | # 新增1 200 | if ('wait_epoach' not in self.order_live_dict[code]): 201 | self.order_live_dict[code].update({'wait_epoach': 0}) 202 | else: 203 | self.order_live_dict[code]['wait_epoach'] = 0 204 | 205 | 206 | else: 207 | if (self.order_live_dict[code]['order_status'] == SELL_ORDER_TBD): 208 | self.order_live_dict[code]['order_status'] = SELL_ORDER_PD 209 | self.order_live_dict[code].update({'sell_order_id': response.order_id}) 210 | if ('wait_epoach' not in self.order_live_dict[code]): 211 | self.order_live_dict[code].update({'wait_epoach': 0}) 212 | else: 213 | self.order_live_dict[code]['wait_epoach'] = 0 214 | 215 | def handle_on_stock_trade(self, trade): 216 | ''' 217 | 成交回报推送处理 218 | :return: 219 | ''' 220 | order_remark = trade.order_remark 221 | side = order_remark[:1] 222 | code = order_remark[1:] 223 | if (side == 'b'): 224 | if ('buy_order_id' in self.order_live_dict[code]): 225 | self.order_live_dict[code]['order_status'] = POS 226 | self.order_live_dict[code].update({'buy_traded_price': trade.traded_price}) 227 | self.order_live_dict[code].update({'pos_epoach':0}) 228 | 229 | else: 230 | self.order_live_dict[code].update({'buy_order_id': trade.order_id}) 231 | self.order_live_dict[code]['order_status'] = POS 232 | self.order_live_dict[code].update({'buy_traded_price': trade.traded_price}) 233 | self.order_live_dict[code].update({'pos_epoach':0}) 234 | 235 | 236 | if ('wait_epoach' in self.order_live_dict[code]): 237 | self.order_live_dict[code]['wait_epoach'] = 0 238 | else: 239 | if ('sell_order_id' in self.order_live_dict[code]): 240 | self.order_live_dict[code]['order_status'] = END 241 | else: 242 | self.order_live_dict[code].update({'sell_order_id': trade.order_id}) 243 | self.order_live_dict[code]['order_status'] = END 244 | if ('wait_epoach' in self.order_live_dict[code]): 245 | self.order_live_dict[code]['wait_epoach'] = 0 246 | 247 | def handle_cancel_respond(self, response): 248 | buy_cancel_dict = {k: v for k, v in self.order_live_dict.items() if v['buy_order_id'] == response.order_id} 249 | sell_cancel_dict = {k: v for k, v in self.order_live_dict.items() if v['sell_order_id'] == response.order_id} 250 | if (len(buy_cancel_dict) == 1): 251 | code = list(buy_cancel_dict.keys())[0] 252 | if (self.order_live_dict[code]['order_status'] == BUY_ORDER_CC_TBD): 253 | if (response.cancel_result == 0): 254 | self.order_live_dict[code]['order_status'] = END 255 | else: 256 | self.order_live_dict[code]['order_status'] = POS 257 | elif (len(sell_cancel_dict) == 1): 258 | code = list(buy_cancel_dict.keys())[0] 259 | if (self.order_live_dict[code]['order_status'] == SELL_ORDER_CC_TBD): 260 | if (response.cancel_result == 0): 261 | self.order_live_dict[code]['order_status'] = POS 262 | else: 263 | self.order_live_dict[code]['order_status'] = END 264 | else: 265 | print('撤单order_id在buy和sell均不存在') 266 | 267 | ''' 268 | 回调类函数------------------------------------------------------------------------------------------------------- 269 | ''' 270 | def create_order_live(self,code): 271 | new_order_live_dict = {code:{'order_status':11}} 272 | self.order_live_dict.update(new_order_live_dict) 273 | return self 274 | 275 | def update_order_pending(self): 276 | #更新所有pending状态的epoach 277 | buy_pending_code_list = [k for k, v in self.order_live_dict.items() if v['order_status'] == BUY_ORDER_PD] 278 | sell_pending_code_list = [k for k, v in self.order_live_dict.items() if v['order_status'] == SELL_ORDER_PD] 279 | 280 | for code in (buy_pending_code_list+sell_pending_code_list): 281 | self.order_live_dict[code]['wait_epoach'] = self.order_live_dict[code]['wait_epoach'] + 1 282 | #更新所有pos epoach 283 | pos_code_list = [k for k, v in self.order_live_dict.items() if (v['order_status'] == POS or v['order_status'] == SELL_ORDER_PD)] 284 | for code in pos_code_list: 285 | self.order_live_dict[code]['pos_epoach'] = self.order_live_dict[code]['pos_epoach'] + 1 286 | 287 | 288 | return self 289 | 290 | 291 | def get_cancel_code_list(self,buy_list,sell_list): 292 | buy_pending_code_list = [k for k, v in self.order_live_dict.items() if v['order_status'] == BUY_ORDER_PD] 293 | sell_pending_code_list = [k for k, v in self.order_live_dict.items() if v['order_status'] == SELL_ORDER_PD] 294 | cancel_buy_pending_code_list = [k for k, v in self.order_live_dict.items() if k in buy_pending_code_list if( 295 | (v['wait_epoach'] >= self.max_buy_epoach and k not in buy_list))] 296 | 297 | cancel_sell_pending_code_list = [k for k, v in self.order_live_dict.items() if k in sell_pending_code_list 298 | if ((v['wait_epoach'] >= self.max_sell_epoach and k not in sell_list))] 299 | return cancel_buy_pending_code_list,cancel_sell_pending_code_list 300 | 301 | # def handle_input_list(self,buy_list,sell_list): 302 | # ''' 303 | # 处理买单 304 | # ''' 305 | # if(len(buy_list)>0): 306 | # buy_list_in_order_live = list(set(buy_list) & set(list(self.order_live_dict.keys()))) 307 | # buy_list_not_in_order_live = list(set(buy_list)-set(buy_list_in_order_live)) 308 | # #将买单分为在dict和不在dict中 309 | # if(len(buy_list_in_order_live)>0): 310 | # buy_dict_in_order_live = {k: v for k, v in self.order_live_dict.items() if k in buy_list_in_order_live} 311 | # result_cancel_sell_dict = dict(filter(lambda x: x[1]['order_status'] in [SELL_ORDER_PD], buy_dict_in_order_live.items())) 312 | # 313 | # #如果在dict中,则对卖单已报的单进行撤单 314 | # else: 315 | # result_cancel_sell_dict = {} 316 | # #如果不在dict中则不用撤 317 | # if(len(buy_list_not_in_order_live)>0 and len(self.order_live_dict)need_buy_num): 320 | # result_send_buy_list = buy_list_not_in_order_live[:need_buy_num] 321 | # else: 322 | # result_send_buy_list = buy_list_not_in_order_live 323 | # #计算不在dict的买单情况 324 | # else: 325 | # result_send_buy_list = [] 326 | # else: 327 | # result_cancel_sell_dict = {} 328 | # result_send_buy_list = [] 329 | # #buy_list为空的情况 330 | # ''' 331 | # 处理卖单 332 | # ''' 333 | # 334 | # if (len(sell_list) > 0): 335 | # sell_list_in_order_live = list(set(sell_list) & set(list(self.order_live_dict.keys()))) 336 | # 337 | # if (len(sell_list_in_order_live) > 0): 338 | # sell_dict_in_order_live = {k: v for k, v in self.order_live_dict.items() if k in sell_list_in_order_live 339 | # } 340 | # result_cancel_buy_dict = dict( 341 | # filter(lambda x: x[1]['order_status'] in [BUY_ORDER_PD], sell_dict_in_order_live.items())) 342 | # 343 | # result_send_sell_dict = dict( 344 | # filter(lambda x: x[1]['order_status'] in [POS], sell_dict_in_order_live.items())) 345 | # 346 | # 347 | # else: 348 | # result_cancel_buy_dict = {} 349 | # result_send_sell_dict = {} 350 | # else: 351 | # result_cancel_buy_dict = {} 352 | # result_send_sell_dict = {} 353 | # return result_cancel_buy_dict,result_cancel_sell_dict,result_send_buy_list,result_send_sell_dict 354 | 355 | def handle_input_list(self, buy_list, sell_list): 356 | ''' 357 | 处理买单 358 | ''' 359 | if (len(buy_list) > 0): 360 | buy_list_in_order_live = list(set(buy_list) & set(list(self.order_live_dict.keys()))) 361 | buy_list_not_in_order_live = list(set(buy_list) - set(buy_list_in_order_live)) 362 | 363 | # 将买单分为在dict和不在dict中 364 | # if (len(buy_list_in_order_live) > 0): 365 | # buy_dict_in_order_live = {k: v for k, v in self.order_live_dict.items() if k in buy_list_in_order_live} 366 | # result_cancel_sell_dict = dict( 367 | # filter(lambda x: x[1]['order_status'] in [SELL_ORDER_PD], buy_dict_in_order_live.items())) 368 | 369 | # 如果在dict中,则对卖单已报的单进行撤单 370 | # else: 371 | # result_cancel_sell_dict = {} 372 | # 如果不在dict中则不用撤 373 | if (len(buy_list_not_in_order_live) > 0 and len(self.order_live_dict) < self.order_live_max_num): 374 | need_buy_num = self.order_live_max_num - len(self.order_live_dict) 375 | if (len(buy_list_not_in_order_live) > need_buy_num): 376 | result_send_buy_list = buy_list_not_in_order_live[:need_buy_num] 377 | else: 378 | result_send_buy_list = buy_list_not_in_order_live 379 | # 计算不在dict的买单情况 380 | else: 381 | result_send_buy_list = [] 382 | else: 383 | # result_cancel_sell_dict = {} 384 | result_send_buy_list = [] 385 | # buy_list为空的情况 386 | ''' 387 | 处理卖单 388 | ''' 389 | 390 | if (len(sell_list) > 0): 391 | result_send_sell_list = [k for k,v in self.order_live_dict.items() if (k in sell_list and v['order_status']==POS)] 392 | # sell_list_in_order_live = list(set(sell_list) & set(list(self.order_live_dict.keys()))) 393 | # 394 | # if (len(sell_list_in_order_live) > 0): 395 | # sell_dict_in_order_live = {k: v for k, v in self.order_live_dict.items() if k in sell_list_in_order_live 396 | # } 397 | # # result_cancel_buy_dict = dict( 398 | # # filter(lambda x: x[1]['order_status'] in [BUY_ORDER_PD], sell_dict_in_order_live.items())) 399 | # 400 | # result_send_sell_dict = dict( 401 | # filter(lambda x: x[1]['order_status'] in [POS], sell_dict_in_order_live.items())) 402 | 403 | else: 404 | # result_cancel_buy_dict = {} 405 | result_send_sell_list = [] 406 | return result_send_buy_list, result_send_sell_list 407 | 408 | 409 | def cut_loss(self): 410 | cancel_order_list = [] 411 | sell_order_list = [] 412 | for key,value in self.order_live_dict.items(): 413 | if(value['order_status']==POS and ( (value['max_bid_price']-value['now_bid_price']>self.move_cut_loss_value) or 414 | (value['pos_epoach']>20 and value['max_bid_price']-value['now_bid_price']>self.move_cut_loss_value/2) ) ): 415 | sell_order_list.append(key) 416 | if(value['order_status']==SELL_ORDER_PD and ( (value['max_bid_price']-value['now_bid_price']>self.move_cut_loss_value) or (value['pos_epoach']>20 and value['max_bid_price']-value['now_bid_price']>self.move_cut_loss_value/2) ) ): 417 | if(value['sell_order_price']>self.core.ask_list_dict[key][-1][0]): 418 | cancel_order_list.append(key) 419 | sell_order_list.append(key) 420 | return cancel_order_list,sell_order_list 421 | 422 | def stop_in_rest_time(self): 423 | today = datetime.datetime.today() 424 | end_time = datetime.datetime(today.year, today.month, today.day, 11, 30, 0) 425 | start_time = datetime.datetime(today.year, today.month, today.day, 13, 00, 0) 426 | if (today > end_time and today < start_time): 427 | time.sleep((start_time - today).total_seconds() + 2) 428 | return self 429 | def change_parm(self,parm_name,value): 430 | if(parm_name=='待成交最大等待轮'): 431 | self.max_epoach = value 432 | elif(parm_name=='最大处理单数'): 433 | self.order_live_max_num = value 434 | elif(parm_name=='搜索间隔时间'): 435 | self.sleep_time = value 436 | elif(parm_name=='每单持仓'): 437 | self.order_amount = value 438 | elif(parm_name=='启动窗口长度'): 439 | self.core.list_window = value 440 | elif(parm_name=='z_score下限'): 441 | self.core.z_score_min = value 442 | elif(parm_name=='z_score上限'): 443 | self.core.z_score_max = value 444 | elif(parm_name=='当前价最小分位值'): 445 | self.core.buy_percentile = value 446 | elif(parm_name=='最低R值'): 447 | self.core.R_min = value 448 | elif(parm_name=='最大p_value'): 449 | self.core.p_value_max = value 450 | elif(parm_name=='最大窗口长度'): 451 | self.core.max_window_length = value 452 | elif(parm_name=='趋向筛选比例'): 453 | self.core.min_trend_filter_dlt = value 454 | elif(parm_name=='penny_jump'): 455 | self.penny_jump = value 456 | elif(parm_name=='penny_jump_mode'): 457 | self.penny_jump_mode = value 458 | else: 459 | print('变量不存在') 460 | return 461 | 462 | def control_change_parm(self,handle_queue): 463 | if(handle_queue.empty()==False): 464 | handle_dict = handle_queue.get() 465 | self.change_parm(handle_dict['parm_name'],handle_dict['value']) 466 | return self 467 | 468 | def send_now_parm_value(self,info_queue): 469 | parm_value_dict = {'启动窗口长度':self.core.list_window,'最大窗口长度':self.core.max_window_length,'当前价最小分位值':self.core.buy_percentile, 470 | '最低R值':self.core.R_min,'最大p_value':self.core.p_value_max,'趋向筛选比例':self.core.min_trend_filter_dlt,'z_score下限': 471 | self.core.z_score_min,'z_score上限':self.core.z_score_max,'最大处理单数':self.order_live_max_num,'每单持仓':self.order_amount, 472 | '待成交最大等待轮':self.max_epoach,'搜索间隔时间':self.sleep_time,'penny_jump':self.penny_jump,'penny_jump_mode':self.penny_jump_mode} 473 | info_queue.put(parm_value_dict) 474 | return self 475 | 476 | def send_detail_value(self,detail_queue): 477 | detail_dict = {'order_live_dict':self.order_live_dict,'z_score_store_dict':self.core.z_score_store_dict,'trade_pair_dict':self.core.trade_pair_dict, 478 | 'tick_series_dict':self.core.tick_series_dict} 479 | detail_queue.put(detail_dict) 480 | return self 481 | #将penny_jump_mode永久关闭 482 | def penny_jump_mode_auto_switch(self): 483 | today = datetime.datetime.today() 484 | penny_end_time = datetime.datetime(today.year, today.month, today.day, 11, 00, 0) 485 | penny_break_time = datetime.datetime(today.year, today.month, today.day, 11, 25, 0) 486 | 487 | if(today>penny_end_time and len(self.order_live_dict)==0): 488 | self.penny_jump_mode = False 489 | elif(today>penny_break_time): 490 | self.penny_jump_mode = False 491 | return 492 | 493 | def trade(self,info_queue,handle_queue): 494 | self.initial_trade() 495 | ''' 496 | 外部初始化区分割线--------------------------------------------- 497 | ''' 498 | self.core = PairTrade() 499 | ''' 500 | 外部初始化区分割线--------------------------------------------- 501 | ''' 502 | while True: 503 | self.stop_in_rest_time() 504 | # self.penny_jump_mode_auto_switch() 505 | 506 | 507 | ''' 508 | 调用区分割线------------------------------------------- 509 | ''' 510 | buy_list,sell_list = self.core.generate_buy_sell_list() 511 | ''' 512 | 调用区分割线------------------------------------------- 513 | ''' 514 | 515 | ''' 516 | 删除已经终止的订单 517 | ''' 518 | if(len(self.order_live_dict)>0): 519 | self.order_live_dict = {k: v for k, v in self.order_live_dict.items() if v['order_status'] != END} 520 | print('order_live_dict为',self.order_live_dict) 521 | ''' 522 | 更新order_live_dict中的max_bid_price和bid_price 523 | ''' 524 | for key,value in self.order_live_dict.items(): 525 | now_bid_price = self.core.bid_list_dict[key][-1][0] 526 | if(self.order_live_dict[key]['max_bid_price']