230504_real_time.py 17 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436
  1. # coding:utf-8
  2. from datetime import datetime as dt
  3. import os
  4. import pandas as pd
  5. from xtquant.xttrader import XtQuantTrader, XtQuantTraderCallback
  6. from xtquant.xttype import StockAccount
  7. from xtquant import xtdata, xtconstant
  8. import time
  9. from sqlalchemy import create_engine, text
  10. from jqdatasdk import *
  11. import pymysql
  12. import multiprocessing as mp
  13. import math
  14. import psutil
  15. import datetime
  16. from apscheduler.schedulers.blocking import BlockingScheduler
  17. import sys
  18. # 指定客户端所在路径
  19. # path = r'c:\\qmt\\userdata_mini'
  20. path = 'C:\\方正证券FQT交易客户端\\userdata_mini'
  21. # 创建资金账号为 800068 的证券账号对象
  22. # acc = StockAccount('920000207040', 'SECURITY')
  23. acc = StockAccount('2162003533')
  24. # 生成session id 整数类型 同时运行的策略不能重复
  25. session_id = 123456
  26. xt_trader = None
  27. engine_stock = create_engine('mysql+pymysql://root:r6kEwqWU9!v3@localhost:3307/qmt_stocks_whole?charset=utf8',
  28. pool_size=5000, pool_recycle=50, max_overflow=-1)
  29. # auth('18616891214', 'Ea?*7f68nD.dafcW34d!')
  30. class MyXtQuantTraderCallback(XtQuantTraderCallback):
  31. def on_disconnected(self):
  32. """
  33. 连接断开
  34. :return:
  35. """
  36. print(datetime.datetime.now(), '连接断开回调')
  37. def on_stock_order(self, order):
  38. """
  39. 委托回报推送
  40. :param order: XtOrder对象
  41. :return:
  42. """
  43. print(datetime.datetime.now(), '委托回调', order.order_remark)
  44. def on_stock_trade(self, trade):
  45. """
  46. 成交变动推送
  47. :param trade: XtTrade对象
  48. :return:
  49. """
  50. print(datetime.datetime.now(), '成交回调', trade.order_remark)
  51. def on_order_error(self, order_error):
  52. """
  53. 委托失败推送
  54. :param order_error:XtOrderError 对象
  55. :return:
  56. """
  57. # print("on order_error callback")
  58. # print(order_error.order_id, order_error.error_id, order_error.error_msg)
  59. print(f"委托报错回调 {order_error.order_remark} {order_error.error_msg}")
  60. def on_cancel_error(self, cancel_error):
  61. """
  62. 撤单失败推送
  63. :param cancel_error: XtCancelError 对象
  64. :return:
  65. """
  66. print(datetime.datetime.now(), sys._getframe().f_code.co_name)
  67. def on_order_stock_async_response(self, response):
  68. """
  69. 异步下单回报推送
  70. :param response: XtOrderResponse 对象
  71. :return:
  72. """
  73. print(f"异步委托回调 {response.order_remark}")
  74. def on_cancel_order_stock_async_response(self, response):
  75. """
  76. :param response: XtCancelOrderResponse 对象
  77. :return:
  78. """
  79. print(datetime.datetime.now(), sys._getframe().f_code.co_name)
  80. def on_account_status(self, status):
  81. """
  82. :param response: XtAccountStatus 对象
  83. :return:
  84. """
  85. print(datetime.datetime.now(), sys._getframe().f_code.co_name)
  86. def run(seq, pid):
  87. mor = datetime.datetime.strptime(
  88. str(dt.now().date()) + '11:30', '%Y-%m-%d%H:%M')
  89. afternoon = datetime.datetime.strptime(
  90. str(dt.now().date()) + '15:00', '%Y-%m-%d%H:%M')
  91. mor_1 = datetime.datetime.strptime(
  92. str(dt.now().date()) + '12:59', '%Y-%m-%d%H:%M')
  93. """阻塞线程接收行情回调"""
  94. import time
  95. client = xtdata.get_client()
  96. while True:
  97. time.sleep(3)
  98. now_date = dt.now()
  99. if not client.is_connected():
  100. xtdata.unsubscribe_quote(seq)
  101. raise Exception('行情服务连接断开')
  102. # if mor < dt.now() < mor_1:
  103. # xtdata.unsubscribe_quote(seq)
  104. # print(f'现在时间:{dt.now()},已休市')
  105. # sys.exit()
  106. # break
  107. # return 0
  108. elif dt.now() > afternoon:
  109. xtdata.unsubscribe_quote(seq)
  110. print(f'现在时间:{dt.now()},已收盘')
  111. sys.exit()
  112. break
  113. # return 0
  114. return
  115. def get_fundamentals(results):
  116. return results
  117. pass
  118. def ma(stock, num, data):
  119. global engine_stock
  120. try:
  121. i = (num - 1) * -1
  122. df = pd.read_sql_query(text(
  123. 'select close_front from `%s_1d`' % stock), engine_stock.connect())
  124. except BaseException as e:
  125. print(e)
  126. return 9999999
  127. else:
  128. ma_num = (sum(df['close_front'][i:]) + data[stock]['lastPrice']) / num
  129. return ma_num
  130. def ma_1(stock, num):
  131. global engine_stock
  132. i = num * -1
  133. try:
  134. df = pd.read_sql_query(text(
  135. 'select close_front from `%s_1d`' % stock), engine_stock.connect())
  136. except BaseException as e:
  137. print(e)
  138. return 9999999
  139. else:
  140. ma_num_1 = df['close_front'][i:].mean()
  141. return ma_num_1
  142. def his_vol(stock, num):
  143. global engine_stock
  144. num = num * -1
  145. try:
  146. df = pd.read_sql_query(text(
  147. 'select volume_front from `%s_1d`' % stock), engine_stock.connect())
  148. except BaseException:
  149. return 9999999
  150. else:
  151. return df['volume_front'].iloc[num]
  152. def ma_judge(data, list_judge, rate, results):
  153. # print(f'这个ma_judge的PID为:{os.getpid()},本轮计算:{len(list_judge)}个股')
  154. for stock in list_judge:
  155. current_price, open_price = data[stock]['lastPrice'], data[stock]['open']
  156. MA5, MA10, MA20, MA30, MA60, MA120 = ma(stock, 5, data), ma(stock, 10, data), ma(stock, 20, data), ma(stock, 30,
  157. data), \
  158. ma(stock, 60, data), ma(stock, 120, data)
  159. MA5_1 = ma_1(stock, 5)
  160. # print(i, current_price, open_price, MA5, MA10, MA20, MA5_1)
  161. # 入交易池标准:阳线\大于MA5\MA5向上\MA20<MA10\离120线有距离
  162. # if (current_price > open_price) & (current_price > MA5) & (MA5 > MA5_1*1.0098) & (current_price < MA5 * 1.05) \
  163. # & (current_price > MA120 or current_price < MA120 * rate) & (MA5 < MA10*0.99):
  164. if (current_price > open_price) & (current_price > MA5) & (
  165. current_price < MA5 * 1.05) \
  166. & (current_price > MA120 or current_price < MA120 * rate):
  167. if his_vol(stock, -1) > his_vol(stock, -2):
  168. results.append(stock.replace('SH', 'XSHG').replace('SZ', 'XSHE'))
  169. def sell_trader(data):
  170. # print('卖出函数:', dt.now())
  171. positions = xt_trader.query_stock_positions(acc)
  172. positions_dict = {positions[x].stock_code: positions[x].can_use_volume for x in range(0, len(positions))}
  173. print(
  174. f'目前持仓总数为:{len([positions[x].stock_code for x in range(0, len(positions)) if positions[x].volume != 0])}')
  175. for stock, can_use_volume in positions_dict.items():
  176. # if stock in data and can_use_volume != 0:
  177. if stock in data:
  178. current_price = data[stock]['lastPrice']
  179. open_price = data[stock]['open']
  180. MA5 = ma(stock, 5, data)
  181. MA5_1 = ma_1(stock, 5)
  182. print(
  183. f"{data[stock]['time']}, {stock}\n当前时间为:{dt.now().strftime('%Y-%m-%d %H:%M:%S')},"
  184. f"信号时间:{dt.fromtimestamp((data[stock]['time']) / 1000.0)}\n"
  185. f"持仓量为{can_use_volume}当前价:{current_price},开盘价:{open_price},"f"MA5:{MA5},昨日MA5:{MA5_1},开始判断:")
  186. df = pd.read_sql_query(text(
  187. 'select close_front, high_front from `%s_1d`' % stock), engine_stock.connect())
  188. if current_price == xtdata.get_instrument_detail(stock).get('UpStopPrice') \
  189. or (df['close_front'].iloc[-1] == df['high_front'].iloc[-1]
  190. and df['close_front'].iloc[-1] / df['close_front'].iloc[-2] > 1.08):
  191. print(f"{stock}涨停或昨日涨幅超过8%,持股观察!{data[stock]['time']}")
  192. continue
  193. elif current_price < MA5 or MA5 < MA5_1:
  194. print('卖出信号!!!!!!', stock, current_price)
  195. order_id = xt_trader.order_stock(acc, stock, xtconstant.STOCK_SELL, can_use_volume,
  196. xtconstant.LATEST_PRICE, 0, 'MA5策略', '低于MA5趋势向下')
  197. print('价格:', current_price, open_price, MA5, MA5_1, '低于MA5趋势向下')
  198. print(order_id, stock, can_use_volume)
  199. elif current_price > MA5 * 1.07:
  200. print('盈利乖离率超7%!!!!!!', stock, current_price)
  201. order_id = xt_trader.order_stock(acc, stock, xtconstant.STOCK_SELL, can_use_volume,
  202. xtconstant.LATEST_PRICE, 0, 'MA5策略', '盈利乖离率超7%')
  203. print('价格:', current_price, open_price, MA5, MA5_1, '盈利乖离率超7%')
  204. print(order_id, stock, can_use_volume)
  205. else:
  206. # print(f'本轮没有持仓股票信息!')
  207. pass
  208. engine_stock.dispose()
  209. def over_avg(stock):
  210. pass
  211. def buy_trader(data):
  212. print('买入函数:', dt.now(), f'接受到{len(data.keys())}个个股')
  213. results = mp.Manager().list()
  214. mp_list = []
  215. engine_hlfx_pool = create_engine('mysql+pymysql://root:r6kEwqWU9!v3@localhost:3307/hlfx_pool?charset=utf8',
  216. pool_pre_ping=True, pool_size=20, pool_timeout=60)
  217. try:
  218. stock_pool = pd.read_sql_query(
  219. text('select value from `%s` order by `index` desc limit 10' % '1d'), engine_hlfx_pool.connect())
  220. stock_pool = stock_pool.iloc[0, 0].split(",")
  221. print(stock_pool)
  222. print('---------')
  223. stock_pool.sort()
  224. # print('stock_pool', len(stock_pool))
  225. except BaseException as e:
  226. print(e)
  227. if len(stock_pool) != 0:
  228. print(set(data.keys()))
  229. list_judge = list(set(data.keys()) & set(stock_pool))
  230. print(f'本轮有{len(data.keys())}条个股信息,而list_judge有:{len(list_judge)}')
  231. else:
  232. print(f'stock_pool为{len(stock_pool)}个')
  233. step = math.ceil(len(list_judge) / 4)
  234. rate = 0.9
  235. if len(list_judge) != 0:
  236. print(f'list_judge:{list_judge}')
  237. for i in range(0, len(list_judge), step):
  238. p = mp.Process(target=ma_judge, args=(data, list_judge[i:i + step], rate, results))
  239. mp_list.append(p)
  240. p.start()
  241. for j in mp_list:
  242. j.join()
  243. results = list(set(results))
  244. engine_hlfx_pool.dispose()
  245. # 选择板块
  246. if len(results) != 0:
  247. print(f'进入板块选择{results}')
  248. # 基本面过滤
  249. results = get_fundamentals(results)
  250. num_industry = get_industry(results)
  251. industry_list = []
  252. for key in num_industry.values():
  253. for key2 in key.values():
  254. industry_list.append(key2['industry_name'])
  255. industry_list = pd.value_counts(industry_list)
  256. # 最热集中的n个板块
  257. max_industry_list = list(industry_list[0:2].index)
  258. results_industry = []
  259. for key, value in num_industry.items():
  260. for key2 in value.values():
  261. if key2['industry_name'] in max_industry_list:
  262. results_industry.append(key)
  263. results_industry = ','.join(set(results_industry))
  264. print('1d', '\n', results_industry)
  265. db_pool = pymysql.connect(host='localhost',
  266. user='root',
  267. port=3307,
  268. password='r6kEwqWU9!v3',
  269. database='hlfx_pool')
  270. cursor_pool = db_pool.cursor()
  271. sql = "INSERT INTO MA5_%s (date,value) VALUES('%s','%s')" % ('1d', dt.now().strftime('%Y-%m-%d %H:%M:%S'),
  272. results_industry)
  273. cursor_pool.execute(sql)
  274. db_pool.commit()
  275. # print(len(results_industry), results_industry)
  276. print(dt.now(), '数据库数据已赋值!')
  277. cursor_pool.close()
  278. db_pool.close()
  279. # 取值交易
  280. keep_stocks = results_industry.split(",")
  281. new_keep_stock = [stock.replace('XSHG', 'SH').replace('XSHE', 'SZ') for stock in keep_stocks]
  282. print(f'{dt.now()},new_keep_stock is:{len(new_keep_stock)}')
  283. if len(new_keep_stock) != 0:
  284. # 进入购买程序
  285. max_pos = 7
  286. for stock in new_keep_stock:
  287. positions = xt_trader.query_stock_positions(acc)
  288. asset = xt_trader.query_stock_asset(acc)
  289. cash = asset.cash
  290. positions_dict = {positions[x].stock_code: positions[x].volume for x in range(0, len(positions)) if
  291. positions[x].volume > 0}
  292. print(f'判断{stock}:cash={cash},持仓数量为{len(positions_dict)}')
  293. current_price = data[stock]['lastPrice']
  294. current_high = data[stock]['high']
  295. if stock not in positions_dict:
  296. overavg = over_avg(stock)
  297. if len(positions_dict) < max_pos and current_price > 9 \
  298. and current_price > (current_high * 0.98):
  299. if 5000 > cash:
  300. i = 1
  301. else:
  302. i = 2
  303. volume = int((cash / i / current_price) // 100 * 100)
  304. print('买入信号!!!!!!', stock, volume, current_price)
  305. order_id = xt_trader.order_stock(acc, stock, xtconstant.STOCK_BUY, volume,
  306. xtconstant.LATEST_PRICE,
  307. current_price, 'MA5策略', 'MA5趋势向上')
  308. print(order_id)
  309. else:
  310. print(f'Cash只有:{cash} 或者 现有持仓{len(positions_dict)} 超过了{max_pos}')
  311. else:
  312. print(f'{stock}已持仓!')
  313. engine_hlfx_pool.dispose()
  314. # print('一轮结束了,现在时间是:', dt.now())
  315. def trader(data):
  316. print(f'本轮订阅{len(data)}', dt.now())
  317. # print(f'xt_trader = {xt_trader},{session_id}')
  318. # print(len(xt_trader.query_stock_positions(acc)))
  319. # 卖出判断
  320. # sell_trader(data)
  321. # 买入条件
  322. buy_trader(data)
  323. def bridge():
  324. global session_id, xt_trader
  325. pid = os.getpid()
  326. connect_result = -1
  327. subscribe_result = -1
  328. while True:
  329. if connect_result != 0 or subscribe_result != 0:
  330. session_id = int(time.time())
  331. xt_trader = XtQuantTrader(path, session_id)
  332. # 创建交易回调类对象,并声明接收回调
  333. callback = MyXtQuantTraderCallback()
  334. xt_trader.register_callback(callback)
  335. # 启动交易线程
  336. xt_trader.start()
  337. # 建立交易连接,返回0表示连接成功
  338. connect_result = xt_trader.connect()
  339. print('建立交易连接,返回0表示连接成功', connect_result)
  340. # 对交易回调进行订阅,订阅后可以收到交易主推,返回0表示订阅成功
  341. subscribe_result = xt_trader.subscribe(acc)
  342. print('对交易回调进行订阅,订阅后可以收到交易主推,返回0表示订阅成功', subscribe_result)
  343. # 建立交易连接,返回0表示连接成功
  344. connect_result = xt_trader.connect()
  345. print('建立交易连接,返回0表示连接成功', connect_result)
  346. # 对交易回调进行订阅,订阅后可以收到交易主推,返回0表示订阅成功
  347. subscribe_result = xt_trader.subscribe(acc)
  348. print('对交易回调进行订阅,订阅后可以收到交易主推,返回0表示订阅成功', subscribe_result)
  349. time.sleep(3)
  350. else:
  351. break
  352. print(f'MyPid is {os.getpid()}, now is {dt.now()},开盘了,session_id = {session_id}, \n')
  353. positions = xt_trader.query_stock_positions(acc)
  354. positions_dict = {positions[x].stock_code: positions[x].can_use_volume for x in range(0, len(positions))}
  355. print(f'今日可卖出个股总数:{len([value for value in positions_dict.values() if value != 0])}')
  356. stocks = xtdata.get_stock_list_in_sector('沪深A股')
  357. # seq_s = xtdata.subscribe_whole_quote(stocks, callback=sell_trader)
  358. seq_b = xtdata.subscribe_whole_quote(stocks, callback=buy_trader)
  359. # run(seq_s, pid)
  360. run(seq_b, pid)
  361. def job_func():
  362. print(f"Job started at {dt.now()}")
  363. # 创建子进程
  364. p = mp.Process(target=bridge)
  365. # 启动子进程
  366. p.start()
  367. # 等待子进程结束
  368. p.join()
  369. print(f"Job finished at {dt.now()}")
  370. if __name__ == '__main__':
  371. mp.freeze_support()
  372. # print('cpu_count =', mp.cpu_count())
  373. pus = psutil.Process()
  374. # pus.cpu_affinity([10, 11, 12, 13, 14, 15])
  375. job_func()
  376. scheduler = BlockingScheduler()
  377. scheduler.add_job(func=job_func, trigger='cron', day_of_week='0-4', hour='09', minute='40',
  378. timezone="Asia/Shanghai", max_instances=5)
  379. # scheduler.add_job(func=job_func, trigger='cron', day_of_week='0-4', hour='12', minute='35',
  380. # timezone="Asia/Shanghai")
  381. try:
  382. scheduler.start()
  383. except (KeyboardInterrupt, SystemExit):
  384. pass