Skip to content

Commit eb25e6b

Browse files
committed
init
1 parent 2051000 commit eb25e6b

16 files changed

+1668
-2
lines changed

README.md

+5-2
Original file line numberDiff line numberDiff line change
@@ -1,2 +1,5 @@
1-
# real_trader
2-
python版本A股实时交易 实时tick数据 + 实盘接口
1+
# 简单的tick级别实时交易 支持聚宽代码完美迁移
2+
##### 依赖 以下类库
3+
##### trade_order 开源的python实盘交易接口
4+
##### trade_bundle 开源的python实时tick行情 以及策略运行框架
5+
##### jqdatasdk 聚宽提供的历史行情

index.py

+40
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,40 @@
1+
# 简单的tick级别实时交易 支持聚宽语法
2+
# 依赖 以下类库
3+
4+
# jqdatasdk 聚宽提供的历史行情
5+
# trade_bundle 开源的python实时tick行情 以及策略运行框架 [后续会提供历史tick数据的接口调用]
6+
# trade_order 开源的python实盘交易接口
7+
from jqdatasdk import *
8+
from trade_bundle.live_trade import *
9+
from trade_order.order_api import *
10+
11+
def initialize(context):
12+
print('##### initialize #####')
13+
14+
# 订阅多个标的
15+
subscribe('600519.XSHG', 'tick')
16+
subscribe('000858.XSHE', 'tick')
17+
18+
# 测试jqdata数据
19+
print(get_price('000001.XSHE', start_date='2015-12-01 14:00:00', end_date='2015-12-02 12:00:00', frequency='1m'))
20+
21+
def before_trading_start(context):
22+
print('##### before_trading_start #####')
23+
24+
def handle_tick(context, tick):
25+
print('##### handle_tick #####')
26+
print(tick.current)
27+
# order('600519.XSHG', 100)
28+
29+
def after_trading_end(context):
30+
print('##### after_trading_end #####')
31+
unsubscribe_all()
32+
33+
# 初始化jqdatasdk
34+
auth('聚宽账号','聚宽密码')
35+
36+
# 初始化实盘模块
37+
init_trader(g, context, '资金账号', '资金密码', r'E:\中泰证券独立下单\xiadan.exe')
38+
39+
# 初始化实时tick行情
40+
init_current_bundle(initialize, before_trading_start, after_trading_end, handle_tick)

requirements.txt

+10
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,10 @@
1+
numpy==1.17.4
2+
pandas==0.25.3
3+
requests==2.22.0
4+
apscheduler==3.6.3
5+
easyutils==0.1.7
6+
jqdatasdk==1.7.8
7+
Pillow==6.2.1
8+
pyautogui==0.9.48
9+
pytesseract==0.3.1
10+
pywinauto==0.6.8

trade_bundle/.DS_Store

6 KB
Binary file not shown.

trade_bundle/live_trade.py

+301
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,301 @@
1+
import requests
2+
import time
3+
import datetime
4+
import json
5+
import pandas as pd
6+
import numpy as np
7+
from apscheduler.schedulers.background import BackgroundScheduler
8+
from apscheduler.schedulers.blocking import BlockingScheduler
9+
10+
# 订阅的标的列表
11+
stock_list = []
12+
session = None
13+
cookies = None
14+
headers = {
15+
'Accept':'*/*',
16+
'Origin':'https://xueqiu.com',
17+
'Referer':'https://xueqiu.com/S/SH600519',
18+
'Sec-Fetch-Mode':'cors',
19+
'User-Agent':'Mozilla/5.0 (Macintosh; Intel Mac OS X 10_12_4) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/77.0.3865.90 Safari/537.36'
20+
}
21+
22+
NAN_DT = datetime.datetime(2200, 1, 1)
23+
24+
class _Global(object):
25+
26+
def __init__(self, **kwargs):
27+
28+
for name, value in kwargs.items():
29+
setattr(self, name, value)
30+
31+
# 通用对象转化
32+
class _Context(object):
33+
34+
def __init__(self, **kwargs):
35+
36+
for name, value in kwargs.items():
37+
setattr(self, name, value)
38+
39+
class Tick(object):
40+
def __init__(self, security, tick):
41+
self._security = parse_xq_code(security)
42+
self._tick = tick
43+
44+
@property
45+
def code(self):
46+
return self._security
47+
48+
@property
49+
def time(self):
50+
try:
51+
return self._tick['time']
52+
except:
53+
return NAN_DT
54+
55+
@property
56+
def current(self):
57+
try:
58+
return self._tick['current']
59+
except:
60+
return np.nan
61+
62+
@property
63+
def high(self):
64+
try:
65+
return self._tick['high']
66+
except:
67+
return np.nan
68+
69+
@property
70+
def low(self):
71+
try:
72+
return self._tick['low']
73+
except:
74+
return np.nan
75+
76+
@property
77+
def trade_volume(self):
78+
try:
79+
return self._tick['trade_volume']
80+
except:
81+
return np.nan
82+
83+
@property
84+
def volume(self):
85+
try:
86+
return self._tick['volume']
87+
except:
88+
return np.nan
89+
90+
@property
91+
def money(self):
92+
try:
93+
return self._tick['money']
94+
except:
95+
return np.nan
96+
97+
# 通用对象转化
98+
class CurrentDict(object):
99+
100+
def __init__(self, **kwargs):
101+
102+
for name, value in kwargs.items():
103+
setattr(self, name, value)
104+
105+
# 当前行情对象
106+
class _CurrentDic(dict):
107+
108+
def __init__(self, date):
109+
pass
110+
111+
def __missing__(self, code):
112+
info = _global['session'].get('https://stock.xueqiu.com/v5/stock/quote.json?extend=detail&symbol=' + parse_code(code), cookies = _global['cookies'], headers = headers).json()
113+
quote = info['data']['quote']
114+
stock = quote['symbol']
115+
result = {
116+
'name': quote['name'],
117+
'time': time.strftime("%Y-%m-%d %H:%M:%S", time.localtime(quote['timestamp'] / 1000)),
118+
'current': quote['current'],
119+
'high': quote['high'],
120+
'low': quote['low'],
121+
'volume': quote['volume'],
122+
'money': quote['amount'],
123+
'day_open': quote['open'],
124+
'high_limit': quote['limit_up'],
125+
'low_limit': quote['limit_down'],
126+
'industry_code': quote['type'],
127+
'is_st': quote['status'] == 2
128+
}
129+
130+
return parse(result)
131+
132+
# 初始化实时行情模块
133+
# 主要工作是 初始化爬虫cookie 初始化tick定时器 完善全局对象
134+
def init_current_bundle(initialize, before_trading_start, after_trading_end, handle_tick):
135+
_global['initialize'] = initialize
136+
_global['before_trading_start'] = before_trading_start
137+
_global['after_trading_end'] = after_trading_end
138+
_global['handle_tick'] = handle_tick
139+
cookies = get_cookie()
140+
141+
# 执行初始化函数
142+
initialize(_global['context'])
143+
144+
# 初始化tick定时器
145+
init_schedudler()
146+
147+
# 创建定时器
148+
# 完成开盘 收盘 盘中3秒批量查询一次最新tick数据 等默认事件
149+
def init_schedudler():
150+
schedudler = BlockingScheduler()
151+
schedudler.add_job(func = _global['before_trading_start'], args = [_global['context']], trigger = 'cron', hour = 9, minute = 9, day_of_week = 'mon-fri')
152+
schedudler.add_job(func = _global['after_trading_end'], args = [_global['context']], trigger = 'cron', hour = 15, minute=30, day_of_week = 'mon-fri')
153+
schedudler.add_job(_get_current_tick, 'cron', second = '*/3')
154+
schedudler.start()
155+
156+
def get_cookie():
157+
cookies = requests.cookies.RequestsCookieJar()
158+
_global['session'] = requests.session()
159+
r = _global['session'].get('https://xueqiu.com/k?q=SZ131810', headers = headers)
160+
_global['cookies'] = r.cookies
161+
return cookies
162+
163+
# 抓取当日历史tick数据
164+
def get_ticks(security, end_dt, count, start_dt = None):
165+
print('### 自定义 get_ticks ###')
166+
result = []
167+
168+
# 大于100取东方财富的 稍后实现
169+
if count > 100:
170+
pass
171+
else:
172+
info = _global['session'].get('https://stock.xueqiu.com/v5/stock/history/trade.json?symbol=' + parse_code(security) + '&count=' + str(count), cookies = _global['cookies'], headers = headers).json()
173+
ticks = info['data']['items']
174+
175+
for tick in ticks:
176+
result.append({
177+
'time': time.strftime("%Y-%m-%d %H:%M:%S", time.localtime(tick['timestamp'] / 1000)),
178+
'current': tick['current'],
179+
'trade_volume': tick['trade_volume'],
180+
})
181+
182+
return result
183+
184+
# 获取最新tick数据
185+
def get_current_tick(stock, df = False):
186+
info = _global['session'].get('https://stock.xueqiu.com/v5/stock/realtime/quotec.json?symbol=' + parse_code(stock), cookies = _global['cookies'], headers = headers).json()
187+
quote = info['data'][0]
188+
stock = quote['symbol']
189+
result = {
190+
'time': time.strftime("%Y-%m-%d %H:%M:%S", time.localtime(quote['timestamp'] / 1000)),
191+
'current': quote['current'],
192+
'high': quote['high'],
193+
'low': quote['low'],
194+
'trade_volume': quote['trade_volume'],
195+
'volume': quote['volume'],
196+
'money': quote['amount']
197+
}
198+
199+
return Tick(stock, result)
200+
201+
# 获取当日最新数据 包含涨停跌停等
202+
def get_current_data():
203+
print('### 自定义 get_current_data ###')
204+
current = _CurrentDic({})
205+
return current
206+
207+
# 获取最新tick数据
208+
def _get_current_tick():
209+
stocks = []
210+
211+
if len(stock_list):
212+
213+
for stock in stock_list:
214+
stocks.append(parse_code(stock))
215+
216+
info = _global['session'].get('https://stock.xueqiu.com/v5/stock/realtime/quotec.json?symbol=' + ','.join(stocks), cookies = _global['cookies'], headers = headers).json()
217+
quotes = info['data']
218+
219+
for quote in quotes:
220+
stock = quote['symbol']
221+
result = {
222+
'time': time.strftime("%Y-%m-%d %H:%M:%S", time.localtime(quote['timestamp'] / 1000)),
223+
'current': quote['current'],
224+
'high': quote['high'],
225+
'low': quote['low'],
226+
'trade_volume': quote['trade_volume'],
227+
'volume': quote['volume'],
228+
'money': quote['amount']
229+
}
230+
_global['handle_tick'](_global['context'], Tick(stock, result))
231+
232+
def parse_code(code):
233+
234+
if code.endswith('XSHE'):
235+
return 'SZ' + code.split('.')[0]
236+
elif code.endswith('XSHG'):
237+
return 'SH' + code.split('.')[0]
238+
239+
def parse_xq_code(code):
240+
241+
if code.startswith('SZ'):
242+
return code[2:8] + '.XSHE'
243+
elif code.startswith('SH'):
244+
return code[2:8] + '.XSHG'
245+
246+
# 要暴露的函数
247+
def subscribe(security, frequency):
248+
print('### 自定义 subscribe ###')
249+
250+
# 加入队列
251+
stock_list.append(security)
252+
print('添加标的到队列 => ', security)
253+
# print('当前订阅的标的队列 => ', stock_list)
254+
255+
# 取消订阅标的的 tick 事件
256+
def unsubcribe(security, frequency):
257+
print('### 自定义 unsubcribe ###')
258+
259+
if security in stock_list:
260+
stock_list.remove(security)
261+
262+
# 取消订阅所有 tick 事件
263+
def unsubscribe_all():
264+
print('### 自定义 unsubscribe_all ###')
265+
stock_list = []
266+
267+
# 定时执行任务
268+
def run_daily(event, time):
269+
_time = time.split(':')
270+
hour = int(_time[0])
271+
minute = int(_time[1])
272+
schedudler = BackgroundScheduler()
273+
schedudler.add_job(func = event, args = [_global['context']], trigger = 'cron', hour = hour, minute = minute, day_of_week = 'mon-fri')
274+
schedudler.start()
275+
276+
## 格式化返回的数据
277+
def parse(data):
278+
result = json.loads(json.dumps(data), object_hook=lambda d: CurrentDict(**d))
279+
return result
280+
281+
## 格式化返回的数据
282+
def _parse_global(data):
283+
result = json.loads(json.dumps(data), object_hook=lambda d: _Global(**d))
284+
return result
285+
286+
## 格式化返回的数据
287+
def _parse_context(data):
288+
result = json.loads(json.dumps(data), object_hook=lambda d: _Context(**d))
289+
return result
290+
291+
# 暴露的全局变量
292+
g = _parse_global({})
293+
context = _parse_context({})
294+
295+
# 保存一些内容
296+
_global = {
297+
'session': None,
298+
'cookies': None,
299+
'context': context,
300+
'g': g
301+
}

trade_order/.DS_Store

6 KB
Binary file not shown.

0 commit comments

Comments
 (0)