【深度学习量化交易19】行情数据获取方式比测(1)——基于miniQMT的量化交易回测系统开发实记
我是Mr.看海,我在尝试用信号处理的知识积累和思考方式做量化交易,应用深度学习和AI实现股票自动交易,目的是实现财务自由~
目前我正在开发基于miniQMT的量化交易系统——看海量化交易系统。
经常使用MiniQMT的朋友都知道,xtquant的行情模块为我们提供了多种数据获取方式,比较常用的就是:
- 单股订阅 subscribe_quote
- 订阅全推行情 subscribe_whole_quote
- 获取全推数据 get_full_tick
在选择具体方法之前,有几个问题不得不弄清楚:
- tick数据分别是多久更新一次?(是不是都是3秒?)
- 使用不同的模式,数据延迟分别是多少?
- K线数据获取有何需要注意之处?
- 这三种方式,在订阅和数据返回方面有和异同?
- 对于运行tick触发、K线触发、自定义时间触发策略,分别用哪种方法最好?(关于这几种触发方式的设计,在之前的文章里有讲过)
实践是检验真理的唯一标准,对此我做了以下几组实验:
一、验证tick数据更新频率
1.1 单股订阅 subscribe_quote
通过下边代码,我订阅了10只沪深股票,并实时打印。
# 测试单股订阅subscribe_quote
from xtquant import xtdata
import time
import datetime# 1.定义回调函数
def on_data(datas):"""数据更新回调函数"""# 获取当前时间(精确到毫秒)current_time = datetime.datetime.now().strftime("%Y-%m-%d %H:%M:%S.%f")[:-3]print(f"\n当前时间: {current_time}")# datas格式为 { stock_code : [data1, data2, ...] }code_list = list(datas.keys())# 遍历所有股票代码及其数据for stock_code in datas:print(f"\n股票: {stock_code}")for data in datas[stock_code]:if 'time' in data:# 将数值时间转换为可读时间(假设time是整数时间戳,单位是秒)time_value = data['time']readable_time = ""# 根据time的格式进行转换if isinstance(time_value, int):if time_value > 10000000000: # 毫秒级时间戳readable_time = datetime.datetime.fromtimestamp(time_value/1000).strftime('%Y-%m-%d %H:%M:%S')else: # 秒级时间戳readable_time = datetime.datetime.fromtimestamp(time_value).strftime('%Y-%m-%d %H:%M:%S')elif isinstance(time_value, str):# 如果是字符串格式,则直接打印readable_time = time_valueprint(f"时间戳: {time_value}, 可读时间: {readable_time}")# 打印完整数据print(data)'''# 在回调中获取完整K线数据klines = xtdata.get_market_data_ex(field_list=["time", "open", "high", "low", "close", "volume"],stock_list=code_list,start_time="20241213",period="1m")print("\n完整K线数据:")print(klines)'''
# 2.订阅10只股票数据
stock_codes = ["000001.SZ", # 平安银行"000002.SZ", # 万科A"000063.SZ", # 中兴通讯"000333.SZ", # 美的集团"000651.SZ", # 格力电器"000858.SZ", # 五粮液"600036.SH", # 招商银行"600519.SH", # 贵州茅台"601318.SH", # 中国平安"601988.SH" # 中国银行
]# 循环订阅每只股票
for stock_code in stock_codes:xtdata.subscribe_quote(stock_code=stock_code,period="tick", # 日K线count=1, # 获取当天所有数据callback=on_data # 设置回调函数)print(f"已订阅: {stock_code}")# 3.保持程序运行
print("开始接收实时数据...")
try:xtdata.run()
except KeyboardInterrupt:print("程序结束")
分析其打印数据,能得到几点结论:
(1)时间戳可能会“早于”系统时间。
例如下边这段数据,在系统时间为11:14:38.898时,就获取到了时间戳为11:14:39的数据。这可能是由于系统时间不准,也可能是服务器发来的数据标记的时间就是超前的。
不过只要和回测所用数据标准一致就行。
当前时间: 2025-03-21 11:14:38.898股票: 000858.SZ
时间戳: 1742526879000, 可读时间: 2025-03-21 11:14:39
{'time': 1742526879000, 'lastPrice': 135.31, 'open': 135.7, 'high': 137.5, 'low': 135.14000000000001, 'lastClose': 135.99, 'amount': 1318754346.0, 'volume': 96871, 'pvolume': 0, 'stockStatus': 3, 'openInt': 13, 'transactionNum': 37317, 'lastSettlementPrice': 0.0, 'settlementPrice': 0.0, 'pe': 0.0, 'askPrice': [135.3, 135.33, 135.34, 135.36, 135.37], 'bidPrice': [135.27, 135.26, 135.23, 135.22, 135.21], 'askVol': [9, 6, 5, 2, 5], 'bidVol': [6, 7, 8, 307, 61], 'volRatio': 0.0, 'speed1Min': 0.0, 'speed5Min': 0.0}
(2)收到的数据每次是一帧,对于同一只股票,获取到数据的时间(秒)可能不是3的倍数。
尽管很多数据的时间戳,秒级都是0、3、6,但是也会有一些是2,5,8等等。
当前时间: 2025-03-21 11:22:02.445股票: 601318.SH
时间戳: 1742527322000, 可读时间: 2025-03-21 11:22:02
{'time': 1742527322000, 'lastPrice': 52.08, 'open': 52.4, 'high': 52.660000000000004, 'low': 52.02, 'lastClose': 52.49, 'amount': 2057214912.0, 'volume': 392893, 'pvolume': 0, 'stockStatus': 3, 'openInt': 13, 'transactionNum': 63022, 'lastSettlementPrice': 0.0, 'settlementPrice': 0.0, 'pe': 0.0, 'askPrice': [52.08, 52.09, 52.1, 52.11, 52.120000000000005], 'bidPrice': [52.07, 52.06, 52.050000000000004, 52.04, 52.03], 'askVol': [24, 173, 111, 61, 153], 'bidVol': [44, 176, 336, 218, 784], 'volRatio': 0.0, 'speed1Min': 0.0, 'speed5Min': 0.0}
(3)订阅量提升后,可能会阻塞程序。
当订阅数量达到300后,程序处理可能会阻塞,其后果需要验证【1】。
1.2 订阅全推行情 subscribe_whole_quote
通过下边的代码,我订阅了全部沪市股票,并实时打印。
# 测试全推数据
from xtquant import xtdata
import time
import datetimedef on_market_data(data):"""全市场数据回调"""# 显示当前时间current_time = datetime.datetime.now().strftime("%Y-%m-%d %H:%M:%S.%f")[:-3]print(f"\n当前时间: {current_time}")# 只打印股票数量和股票的数据stock_count = len(data)print(f"收到{stock_count}只股票的数据")if data: # 如果有数据# 获取最多10只股票的数据stock_list = list(data.keys())[:]print(f"显示前{len(stock_list)}只股票的数据:")for i, stock_code in enumerate(stock_list, 1):stock_data = data[stock_code]# 转换时间戳为可读时间timestamp_ms = stock_data['time']readable_time = datetime.datetime.fromtimestamp(timestamp_ms/1000).strftime("%Y-%m-%d %H:%M:%S.%f")[:-3]print(f"\n{i}. 股票 {stock_code} 的数据时间: {readable_time}")print(f"数据详情:")print(stock_data)
# 订阅沪深两市的tick数据
seq = xtdata.subscribe_whole_quote(code_list=['SH'], # 上海市场callback=on_market_data
)
# 运行程序
print("开始接收数据...")
try:xtdata.run()
except KeyboardInterrupt:xtdata.unsubscribe_quote(seq)print("程序结束")
得到的回调数据不是沪市股票数据同时发来,而是“一堆一堆”地,通常“一堆”数据中有几十只股票。
当前时间: 2025-03-20 13:52:28.895
收到56只股票的数据
对于其中的单只股票,又呈现以下几种现象:
(1)结论1:tick以不严格等于3s的时间间隔获取
股票 600132.SH 的数据时间: 2025-03-20 13:52:04.000
数据详情:
{'time': 1742449924000, 'lastPrice': 58.42, 'open': 60.0, 'high': 60.0, 'low': 58.410000000000004, 'lastClose': 59.7, 'amount': 215877600.0, 'volume': 36620, 'pvolume': 3661980, 'stockStatus': 0, 'openInt': 13, 'transactionNum': 0, 'lastSettlementPrice': 0.0, 'settlementPrice': 0.0, 'pe': 0.0, 'askPrice': [58.45, 0.0, 0.0, 0.0, 0.0], 'bidPrice': [58.42, 0.0, 0.0, 0.0, 0.0], 'askVol': [49, 0, 0, 0, 0], 'bidVol': [58, 0, 0, 0, 0], 'volRatio': 0.0, 'speed1Min': 0.0, 'speed5Min': 0.0}
股票 600132.SH 的数据时间: 2025-03-20 13:52:07.000
数据详情:
{'time': 1742449927000, 'lastPrice': 58.45, 'open': 60.0, 'high': 60.0, 'low': 58.410000000000004, 'lastClose': 59.7, 'amount': 215895100.0, 'volume': 36623, 'pvolume': 3662280, 'stockStatus': 0, 'openInt': 13, 'transactionNum': 0, 'lastSettlementPrice': 0.0, 'settlementPrice': 0.0, 'pe': 0.0, 'askPrice': [58.45, 0.0, 0.0, 0.0, 0.0], 'bidPrice': [58.43, 0.0, 0.0, 0.0, 0.0], 'askVol': [47, 0, 0, 0, 0], 'bidVol': [3, 0, 0, 0, 0], 'volRatio': 0.0, 'speed1Min': 0.0, 'speed5Min': 0.0}
股票 600132.SH 的数据时间: 2025-03-20 13:52:10.000
数据详情:
{'time': 1742449930000, 'lastPrice': 58.42, 'open': 60.0, 'high': 60.0, 'low': 58.410000000000004, 'lastClose': 59.7, 'amount': 216082100.0, 'volume': 36655, 'pvolume': 3665480, 'stockStatus': 0, 'openInt': 13, 'transactionNum': 0, 'lastSettlementPrice': 0.0, 'settlementPrice': 0.0, 'pe': 0.0, 'askPrice': [58.44, 0.0, 0.0, 0.0, 0.0], 'bidPrice': [58.43, 0.0, 0.0, 0.0, 0.0], 'askVol': [3, 0, 0, 0, 0], 'bidVol': [1, 0, 0, 0, 0], 'volRatio': 0.0, 'speed1Min': 0.0, 'speed5Min': 0.0}
22. 股票 600132.SH 的数据时间: 2025-03-20 13:52:14.000
数据详情:
{'time': 1742449934000, 'lastPrice': 58.42, 'open': 60.0, 'high': 60.0, 'low': 58.410000000000004, 'lastClose': 59.7, 'amount': 216123000.0, 'volume': 36662, 'pvolume': 3666180, 'stockStatus': 0, 'openInt': 13, 'transactionNum': 0, 'lastSettlementPrice': 0.0, 'settlementPrice': 0.0, 'pe': 0.0, 'askPrice': [58.44, 0.0, 0.0, 0.0, 0.0], 'bidPrice': [58.42, 0.0, 0.0, 0.0, 0.0], 'askVol': [2, 0, 0, 0, 0], 'bidVol': [25, 0, 0, 0, 0], 'volRatio': 0.0, 'speed1Min': 0.0, 'speed5Min': 0.0}
13. 股票 600132.SH 的数据时间: 2025-03-20 13:52:16.000
数据详情:
{'time': 1742449936000, 'lastPrice': 58.44, 'open': 60.0, 'high': 60.0, 'low': 58.410000000000004, 'lastClose': 59.7, 'amount': 216187200.0, 'volume': 36673, 'pvolume': 3667280, 'stockStatus': 0, 'openInt': 13, 'transactionNum': 0, 'lastSettlementPrice': 0.0, 'settlementPrice': 0.0, 'pe': 0.0, 'askPrice': [58.44, 0.0, 0.0, 0.0, 0.0], 'bidPrice': [58.42, 0.0, 0.0, 0.0, 0.0], 'askVol': [9, 0, 0, 0, 0], 'bidVol': [15, 0, 0, 0, 0], 'volRatio': 0.0, 'speed1Min': 0.0, 'speed5Min': 0.0}
比如上边这个例子,时间戳的秒级分别为4、7、10、14、16,时间间隔出现了2、3、4秒这几种情况。经验证,后期下载下来的数据的时间戳与此一致。
(2)结论2:可能会出现“漏拍”
12. 股票 600177.SH 的数据时间: 2025-03-20 13:52:14.000
数据详情:
{'time': 1742449934000, 'lastPrice': 8.07, 'open': 8.120000000000001, 'high': 8.13, 'low': 8.07, 'lastClose': 8.11, 'amount': 145295300.0, 'volume': 179710, 'pvolume': 17970989, 'stockStatus': 0, 'openInt': 13, 'transactionNum': 0, 'lastSettlementPrice': 0.0, 'settlementPrice': 0.0, 'pe': 0.0, 'askPrice': [8.08, 0.0, 0.0, 0.0, 0.0], 'bidPrice': [8.07, 0.0, 0.0, 0.0, 0.0], 'askVol': [1816, 0, 0, 0, 0], 'bidVol': [9792, 0, 0, 0, 0], 'volRatio': 0.0, 'speed1Min': 0.0, 'speed5Min': 0.0}
14. 股票 600177.SH 的数据时间: 2025-03-20 13:52:17.000
数据详情:
{'time': 1742449937000, 'lastPrice': 8.08, 'open': 8.120000000000001, 'high': 8.13, 'low': 8.07, 'lastClose': 8.11, 'amount': 145303400.0, 'volume': 179720, 'pvolume': 17971989, 'stockStatus': 0, 'openInt': 13, 'transactionNum': 0, 'lastSettlementPrice': 0.0, 'settlementPrice': 0.0, 'pe': 0.0, 'askPrice': [8.08, 0.0, 0.0, 0.0, 0.0], 'bidPrice': [8.07, 0.0, 0.0, 0.0, 0.0], 'askVol': [1829, 0, 0, 0, 0], 'bidVol': [9809, 0, 0, 0, 0], 'volRatio': 0.0, 'speed1Min': 0.0, 'speed5Min': 0.0}
16. 股票 600177.SH 的数据时间: 2025-03-20 13:52:23.000
数据详情:
{'time': 1742449943000, 'lastPrice': 8.07, 'open': 8.120000000000001, 'high': 8.13, 'low': 8.07, 'lastClose': 8.11, 'amount': 145304200.0, 'volume': 179721, 'pvolume': 17972089, 'stockStatus': 0, 'openInt': 13, 'transactionNum': 0, 'lastSettlementPrice': 0.0, 'settlementPrice': 0.0, 'pe': 0.0, 'askPrice': [8.08, 0.0, 0.0, 0.0, 0.0], 'bidPrice': [8.07, 0.0, 0.0, 0.0, 0.0], 'askVol': [1829, 0, 0, 0, 0], 'bidVol': [9808, 0, 0, 0, 0], 'volRatio': 0.0, 'speed1Min': 0.0, 'speed5Min': 0.0}
上边的例子可以看出,13:52:17到13:52:23之间,漏掉了“一拍”。这可能是由于这“一拍”没有成交量引起的。
为了验证是否是此原因,我下载了当前的tick数据,这几秒的数据为:
2025-03-20,13:52:14,8.07,8.12,8.13,8.07,8.11,145295288.0,179710,0,0,13,0.0,"[8.08, 8.09, 8.1, 8.11, 8.12]","[8.07, 8.06, 8.05, 8.04, 8.03]","[1816, 3285, 2861, 3111, 3116]","[9792, 12223, 9316, 3182, 5398]"
2025-03-20,13:52:17,8.08,8.12,8.13,8.07,8.11,145303368.0,179720,0,0,13,0.0,"[8.08, 8.09, 8.1, 8.11, 8.12]","[8.07, 8.06, 8.05, 8.04, 8.03]","[1829, 3264, 2861, 3111, 3116]","[9809, 12223, 9316, 3182, 5398]"
2025-03-20,13:52:20,8.08,8.12,8.13,8.07,8.11,145303368.0,179720,0,0,13,0.0,"[8.08, 8.09, 8.1, 8.11, 8.12]","[8.07, 8.06, 8.05, 8.04, 8.03]","[1829, 3264, 2861, 3111, 3116]","[9809, 12223, 9286, 3182, 5398]"
2025-03-20,13:52:23,8.07,8.12,8.13,8.07,8.11,145304175.0,179721,0,0,13,0.0,"[8.08, 8.09, 8.1, 8.11, 8.12]","[8.07, 8.06, 8.05, 8.04, 8.03]","[1829, 3264, 2861, 3111, 3116]","[9808, 12223, 9285, 3182, 5398]"
可见13:52:20的数据和上一个tick的数据完全相同,全推作为增量补充的手段,就会不再推送这一帧数据。
1.3 获取全推数据 get_full_tick
全推数据的测试程序是这样的:
# fulltick行情获取,测试数据更新频率和所谓增量补充数据的概念
from xtquant import xtdata
import time
import datetime# 循环获取沪深两市的最新行情快照
try:while True:# 记录当前时间start_time = time.time()current_time = datetime.datetime.now().strftime("%Y-%m-%d %H:%M:%S.%f")[:-3]# 获取沪深两市的最新行情快照snapshot = xtdata.get_full_tick(["SH"])# 记录获取快照后的时间snapshot_time = datetime.datetime.now().strftime("%Y-%m-%d %H:%M:%S.%f")[:-3]# 计算耗时end_time = time.time()elapsed_time = (end_time - start_time) * 1000 # 转换为毫秒# 打印时间和耗时print(f"\n当前时间: {current_time}")print(f"获取快照后时间: {snapshot_time}")print(f"执行get_full_tick耗时: {elapsed_time:.2f}毫秒")# 打印股票数据print(f"当前市场共{len(snapshot)}只股票")if snapshot:# 展示前3只股票的数据stock_list = list(snapshot.keys())[:3]for i, stock_code in enumerate(stock_list, 1):print(f"\n{i}. 股票{stock_code}的tick数据:")print(snapshot[stock_code])# 等待0.05秒再次执行#time.sleep(0.05)
except KeyboardInterrupt:print("\n程序已终止")
同样订阅了沪市的全部股票,经验证同样基本是3秒刷新一次。不过这个函数得到的全截面横扫,不会有“跳帧”的情况。
如果在一个tick的3秒钟内不断地获取全推数据,其中会有很多帧数据都是重复的。
二、验证tick数据延迟
这个延迟包含两个层面,一是获取某一秒时间戳的tick数据时,此时的时间延迟多少;二是时间戳标注的数据相对于真实的数据延迟是多少。
对于第二个问题,属于迅投数据本身的质量问题,暂时无法验证。
对于第一个问题,则可以进行对比。同样使用上述的程序,能得到下边的结果:
2.1 单股订阅 subscribe_quote
单股订阅的延迟来自于订阅数量较大时,数据会逐一触发回调。就当前测试程序订阅的10只股票来看,时间戳的时间和系统时间相差均在正负零点几毫秒的误差范围内。
2.2 订阅全推行情 subscribe_whole_quote
大体来说延迟不算太大,时间戳的时间和系统时间相差均在正负零点几毫秒的误差范围内。
2.3 获取全推数据 get_full_tick
对于全部7879只股票,获取到的耗时大概为250毫秒。
三、K线数据获取有何需要注意之处?
3.1 单股订阅 subscribe_quote
经过测试,获取K线数据时(比如1m数据),也是每3秒进入一次回调。
不过经过实测,有两点需要注意:
(1)K线数据内的数据,指的是该周期计算起始点到当前时间点的统计值。
举个例子,下边这两帧信号是一分钟中的最后两帧和下一秒信号的第一帧,例如其中的volume和amount是随着时间的推移增加的,当达到一分钟的最后一帧时,即为该分钟最终的K线数据。下一秒volume和amount重新开始计数,对于其他的参数也是如此。
不过帧数据的时间戳都是精确到分,而不是秒,这点需要注意。
当前时间: 2025-03-20 14:54:55.741股票: 600036.SH
时间戳: 1742453700000, 可读时间: 2025-03-20 14:55:00
{'time': 1742453700000, 'open': 45.22, 'high': 45.24, 'low': 45.19, 'close': 45.21, 'volume': 2571, 'amount': 11626401.0, 'settlementPrice': 0.0, 'openInterest': 13, 'dr': 1.0, 'totaldr': 5.2226309371267226, 'preClose': 6.9523421295223e-310, 'suspendFlag': 0}当前时间: 2025-03-20 14:54:58.708股票: 600036.SH
时间戳: 1742453700000, 可读时间: 2025-03-20 14:55:00
{'time': 1742453700000, 'open': 45.22, 'high': 45.24, 'low': 45.19, 'close': 45.230000000000004, 'volume': 2860, 'amount': 12933130.0, 'settlementPrice': 0.0, 'openInterest': 13, 'dr': 1.0, 'totaldr': 5.2226309371267226, 'preClose': 6.9523421295223e-310, 'suspendFlag': 0}股票: 600036.SH
时间戳: 1742453760000, 可读时间: 2025-03-20 14:56:00
{'time': 1742453760000, 'open': 45.230000000000004, 'high': 45.230000000000004, 'low': 45.230000000000004, 'close': 45.230000000000004, 'volume': 157, 'amount': 710015.0, 'settlementPrice': 0.0, 'openInterest': 13, 'dr': 1.0, 'totaldr': 5.2226309371267226, 'preClose': 6.9523421295223e-310, 'suspendFlag': 0}
(2)K线最后一帧数据代表了该周期下K线数据的最终值
标题即是结论,我在盘后下载的数据中对比了。
需要注意的是,一分钟的最后一帧大致在57秒后到达,接收完10组数据大概用了1.8秒。这其中可能打印占用了一定时间,不过在订阅数达到了普通端口权限上线(300只)时,数据接收本身耗时有多长,是需要继续探究的问题【1】,因为过长的接受周期,可能会导致程序阻塞。
3.2 订阅全推行情 subscribe_whole_quote、获取全推数据 get_full_tick
这两个方法均只能获取tick数据。
四、这三种方式,在订阅和数据返回方面有和异同?
4.1 单股订阅 subscribe_quote
所谓单股订阅,自然每次只能订阅一只股票了,为了实现多只股票的订阅,需要使用循环来达到目的。
由于每只股票数据都要单独触发一次回调函数,该过程变得有些低效。对于少量股票订阅该效率尚可,股票数量多了后,可能会带来不能接受的低效。(此处需验证【1】)
4.2 订阅全推行情 subscribe_whole_quote
批量订阅,“成堆”返回数据,数据获取既高效又及时,在大批量数据订阅时的不二选择。
4.3 获取全推数据 get_full_tick
其实数据获取的延迟尚可接受,但是相对于订阅全推行情,总还是难免有滞后。可以在实时性要求不高的场景下作为补充手段,可精确控制查询频率,避免推送机制可能带来的数据洪峰。
五、对于运行tick触发、K线触发、自定义时间触发策略,分别用哪种方法最好?
5.1 tick触发
最佳方法:订阅全推行情 subscribe_whole_quote
次选方法:单股订阅subscribe_quote
也就是两种方式可兼具,根据订阅的股票数量和策略场景自由选择。其实所谓“订阅全推行情”,并不是一定要把沪深的股票全部订阅,是可以指定要订阅的股票代码的。
5.2 K线触发
最佳方法:单股订阅subscribe_quote
理由很简单,另外两种方式无法获取K线数据。
不过需要注意的一点是,单股订阅K线数据,需要能够筛选出K线周期最后一帧的数据,这个在算法上需要有所设计。
其实使用全推的方式也可以模拟计算出K线的效果,但是这样做效率既不高,实现又相对复杂,所以就不考虑了。
5.3 自定义时间触发
最佳方法:单股订阅subscribe_quote+get_market_data_ex、获取全推数据 get_full_tick
次选方法:订阅全推行情 subscribe_whole_quote
自定义时间触发和上述两种触发方式有着本质上的区别:tick或者K线触发都是以数据进行驱动的,即数据到位后触发回调;而自定义时间触发时以时间为驱动的,即到某一时刻点之后自动进入回调程序。
所以对于自定义时间触发的方式,需要在回调程序(或者说策略的主程序)中获取数据。此时如果需要获取的是市场横截面的tick数据,直接使用get_full_tick即可;如果要获取的是K线数据(tick数据也可以),可以通过单股订阅(在初始化程序时订阅),然后通过get_market_data_ex读取数据的方式实现。
至于能够使用subscribe_whole_quote订阅全市场行情,然后使用get_market_data_ex读取数据,这个有待验证是否可行【2】。
注:【1】【2】后续会写一篇文章单独测试单股订阅数达到上限时,获取数据的效率表现;以及测试订阅全市场行情,能否使用get_market_data_ex读取数据。
六、其他
还需要讨论的一个问题是,使用上述订阅/数据获取方式,是否能在回测时回测最大程度还原实盘,其中tick触发是相对简单的,只要数据不阻塞,每一个tick能被正确处理就行;K线数据则需要设计一个比较合理的最后一帧的确认逻辑;自定义时间触发存在一个较为难处理的问题,即 get_full_tick这个函数在回测系统中不易模拟,这需要提前下载全盘的tick数据,再封装一个函数调用当前时间戳下的最新tick数据,一来需要调动处理的数据量巨大,另外能够与真实情况下的全推数据完全保持一致,是较难验证和实现的。
七、下一步考虑
后边还有几件事要做,做完后回测系统就可以跟大家见面了:
- 策略框架标准化定义
- 策略通用工具箱完善
- 策略的项目化管理设置
- 日志管理
- 回测结果可视化与评估参数优化
- 与成熟的回测软件(比如QMT)进行相同策略的对比,以验证软件的有效性
因此,目前的回测系统还不满足放出来给大家使用的状态,待测试稳定后,快捷的安装包版本以及全部开源代码都会放出来给读者朋友们使用。
近期我尽量加快软件和文章更新的频率,尽早让朋友们使用上这个软件。
end、开通miniQMT
上述讲到的系统是基于miniQMT,很多券商都可以开通miniQMT,不过门槛各有不同,很多朋友找不到合适的券商和开通渠道。这里我可以联系券商渠道帮忙开通,股票交易费率是万1,开通成功的朋友都可以免费使用上边开发的“看海量化交易系统”。这个系统还在持续开发的过程中,数据下载的功能已经可以使用,回测部分正在加紧开发,大家可以先开通MiniQMT的权限,这样回测部分的功能放出后就能第一时间用上了~
对于想要开通miniQMT、使用上边开发的“看海量化交易系统”的朋友们,请大家关注一下我的公众号“看海的城堡”,在公众号页面下方点击相应标签即可获取。
相关文章
【深度学习量化交易1】一个金融小白尝试量化交易的设想、畅享和遐想
【深度学习量化交易2】财务自由第一步,三个多月的尝试,找到了最合适我的量化交易路径
【深度学习量化交易3】为了轻松免费地下载股票历史数据,我开发完成了可视化的数据下载模块
【深度学习量化交易4】 量化交易历史数据清洗——为后续分析扫清障碍
【深度学习量化交易5】 量化交易历史数据可视化模块
【深度学习量化交易6】优化改造基于miniQMT的量化交易软件,已开放下载~(已完成数据下载、数据清洗、可视化模块)
【深度学习量化交易7】miniQMT快速上手教程案例集——使用xtQuant进行历史数据下载篇
【深度学习量化交易8】miniQMT快速上手教程案例集——使用xtQuant进行获取实时行情数据篇
【深度学习量化交易9】miniQMT快速上手教程案例集——使用xtQuant获取基本面数据篇
【深度学习量化交易10】miniQMT快速上手教程案例集——使用xtQuant获取板块及成分股数据篇
【深度学习量化交易11】miniQMT快速上手教程——使用XtQuant进行实盘交易篇(一万七千字超详细版本)
【深度学习量化交易12】基于miniQMT的量化交易框架总体构建思路——回测、模拟、实盘通吃的系统架构
【深度学习量化交易13】继续优化改造基于miniQMT的量化交易软件,增加补充数据功能,优化免费下载数据模块体验!
【深度学习量化交易14】正式开源!看海量化交易系统——基于miniQMT的量化交易软件
【深度学习量化交易15】基于miniQMT的量化交易回测系统已基本构建完成!AI炒股的框架初步实现
【深度学习量化交易16】韭菜进阶指南:A股交易成本全解析
【深度学习量化交易17】触发机制设置——基于miniQMT的量化交易回测系统开发实记
【深度学习量化交易18】盘前盘后回调机制设计与实现——基于miniQMT的量化交易回测系统开发实记
相关文章:
【深度学习量化交易19】行情数据获取方式比测(1)——基于miniQMT的量化交易回测系统开发实记
我是Mr.看海,我在尝试用信号处理的知识积累和思考方式做量化交易,应用深度学习和AI实现股票自动交易,目的是实现财务自由~ 目前我正在开发基于miniQMT的量化交易系统——看海量化交易系统。 经常使用MiniQMT的朋友都知道,xtquant的…...
《网络管理》实践环节03:snmp服务器上对网络设备和服务器进行初步监控
兰生幽谷,不为莫服而不芳; 君子行义,不为莫知而止休。 应用拓扑图 3.0准备工作 所有Linux服务器上(服务器和Agent端)安装下列工具 yum -y install net-snmp net-snmp-utils 保证所有的HCL网络设备和服务器相互间能…...
linux操作系统
1.linux进程管理 操作系统都有进程的概念 查看和关闭程序 2.关闭进程 3,查看计算机硬件信息 4.定时任务...
Python基础语法 - 判断语句
Python基础语法 - 判断语句 1. if语句 if 条件:# 条件为True时执行的代码示例 age 18 if age > 18:print("您已成年")2. if-else语句 if 条件:# 条件为True时执行的代码 else:# 条件为False时执行的代码示例 age 16 if age > 18:print("您已成年&q…...
c++柔性数组、友元、类模版
目录 1、柔性数组: 2、友元函数: 3、静态成员 注意事项 面试题:c/c static的作用? C语言: C: 为什么可以创建出 objx 4、对象与对象之间的关系 5、类模版 1、柔性数组: #define _CRT_SECURE_NO_WARNINGS #…...
电子技术基础
目录 一、整体概述 二、知识点梳理及考点分析 (一)半导体器件 (二)基本放大电路 (三)功率放大电路 (四)集成运算放大器 (五)直流稳压电源 ࿰…...
解码大模型时代算力基座的隐形引擎
在千亿参数大模型竞速的今天,算力军备竞赛已进入白热化阶段。当我们聚焦GPU集群的运算峰值时,一个关键命题正在浮出水面:支撑大模型全生命周期的存力基座,正在成为制约AI进化的关键变量。绿算技术将深入解剖大模型训练与推理场景中…...
【NetCore】ControllerBase:ASP.NET Core 中的基石类
ControllerBase:ASP.NET Core 中的基石类 一、什么是 ControllerBase?二、ControllerBase 的主要功能三、ControllerBase 的常用属性四、ControllerBase 的常用方法2. 模型绑定与验证3. 依赖注入五、ControllerBase 与 Controller 的区别六、实际开发中的最佳实践七、总结在 …...
人工智能之数学基础:矩阵分解之LU分解
本文重点 LU分解是线性代数中一种重要的矩阵分解方法,它将一个方阵分解为一个下三角矩阵(L)和一个上三角矩阵(U)的乘积。这种分解方法在数值线性代数中有着广泛的应用,特别是在求解线性方程组、计算矩阵的行列式、求逆矩阵等方面。 LU分解的基本概念 设A是一个nn的方阵…...
C#核心学习(六)面向对象--封装(5)静态成员及静态构造函数和静态类 以及和常量的区别
目录 一、什么是静态的?什么是常量? 1. 静态(Static) 2. 常量(const) 二、类中的静态成员有什么用? 1. 共享数据 2. 工具方法与全局配置 3. 单例模式 三、静态类和静态成…...
去中心化稳定币机制解析与产品策略建议
去中心化稳定币机制解析与产品策略建议(以Maker/DAI为例) 一、核心机制对比:法币抵押型 vs. 加密货币抵押型 法币抵押型(如USDT) 技术逻辑:1:1美元储备托管于中心化机构(如银行)&…...
构造超小程序
文章目录 构造超小程序1 编译器-大小优化2 编译器-移除 C 异常3 链接器-移除所有依赖库4 移除所有函数依赖_RTC_InitBase() _RTC_Shutdown()__security_cookie __security_check_cookie()__chkstk() 5 链接器-移除清单文件6 链接器-移除调试信息7 链接器-关闭随机基址8 移除异常…...
JSONP跨域访问漏洞
一、漏洞一:利用回调GetCookie <?php$conn new mysqli(127.0.0.1,root,root,learn) or die("数据库连接不成功"); $conn->set_charset(utf8); $sql "select articleid,author,viewcount,creattime from learn3 where articleid < 5"; $result…...
数据结构优化DP总结
单调栈:Codeforces Round 622 (Div. 2) C2. Skyscrapers (hard version) 简单来讲就是最后需要呈现出一个单峰数组,使得总高度最高。 最开始想到暴力枚举每一个元素都充当最高的“单峰”,但是这里的 n 过大,这样枚举肯定会TLE。 …...
Linux网络相关概念和重要知识(4)(序列化和反序列化、TCP协议、会话和守护进程)
目录 1.序列化和反序列化 (1)为什么需要序列化 (2)序列化方案 ①json ②json序列化代码模板 ③json反序列化代码模板 ④将自定义方案和json结合 2.TCP协议(传输控制协议) (1)…...
[MySQL初阶]MySQL数据库基础
MySQL数据库基础 1. 数据库基础1.1 什么是数据库1.2 主流数据库2. 数据库的基本使用2.1 连接服务器2.2 使用案例2.3 数据逻辑存储3. MySQL架构与分类3.1 MySQL架构3.2 SQL分类4. 存储引擎4.1 存储引擎基本概念4.2 存储引擎基本操作1. 数据库基础 1.1 什么是数据库 存储数据用…...
【mysql 的安装及使用】
MySQL 9.0 一、下载MySQL[MySQL 9.0 下载] [(https://dev.mysql.com/downloads/mysql/)选择自定义,选择合适安装路径二、检查安装情况配置环境变量打开命令行查看版本创建数据库在MySQL中,可以使用create database语句来创建数据库。以下是创建一个名为my_db的数据库的示例:…...
d202542
一、142.环形链表I 142. 环形链表 II - 力扣(LeetCode) 用set统计一下 如果再次出现那么就环的第一个return返回就行 public ListNode detectCycle(ListNode head) {Set<ListNode> set new HashSet<>();ListNode cur head;while(cur ! …...
vscode代码片段的设置与使用
在 Visual Studio Code (VS Code) 中,可以通过自定义**代码片段(Snippets)**快速插入常用代码模板。以下是详细设置步骤: 步骤 1:打开代码片段设置 按下快捷键 Ctrl Shift P(Windows/Linux)或…...
3D 地图渲染-区域纹理图添加
引入-初始化地图(关键代码) // 初始化页面引入高德 webapi -- index.html 文件 <script src https://webapi.amap.com/maps?v2.0&key您申请的key值></script>// 添加地图容器 <div idcontainer ></div>// 地图初始化应该…...
spring-security原理与应用系列:HttpSecurity.filters
目录 AnyRequestMatcher WebSecurityConfig HttpSecurity AbstractInterceptUrlConfigurer AbstractAuthenticationProcessingFilter 类图 在前面的文章《spring-security原理与应用系列:securityFilterChainBuilders》中,我们遗留了一个问题&…...
每日总结4.2
蓝桥杯刷题: 1. 方格分割(dfs,选中心点,开始上下左右遍历,达到边界时数量加一) #include <bits/stdc.h> using namespace std; bool vis[10][10]; int mp[10][10]; int ans0; int dx[4]{1,0,0,-1}; int dy[4]{…...
架构师面试(二十五):分布式存储 Leader 设计
问题 在非常多的分布式存储系统中,如:Zookeeper、Etcd、Kafka等,往往会存在一个 【Leader】 角色,并由该角色负责数据的写入,这样设计最主要的原因是什么呢? A. 唯一负责数据写入的 Leader 角色可以避免并…...
mycat --分片规则--
文章目录 MyCat分片规则详解1. rule1 (基于id的func1算法)2. sharding-by-date (按日期分片)3. rule2 (基于user_id的func1算法)4. sharding-by-intfile (基于枚举值分片)5. auto-sharding-long (长整型范围分片)6. mod-long (取模分片)7. sharding-by-murmur (MurmurHash分片)…...
系统分析师备考启动
以考促学:软件高级系统分析师。 一、考试目的: 1、练习三遍读书法、快速阅读、番茄工作法、第一性原理、思维导图等学习方法和学习工具的使用。 2、掌握知识、编织知识网、顺便拿证。 二、组织形式: 小组统一安排学习内容,每…...
轻量级搜索接口技术解析:快速实现关键词检索的Java/Python实践
Hi,你好! 轻量级搜索接口技术解析:快速实现关键词检索的Java/Python实践 接口特性与适用场景 本接口适用于需要快速集成搜索能力的开发场景,支持通过关键词获取结构化搜索结果。典型应用场景包括: 垂直领域信息检索…...
防爆风扇选型指南:根据风量风压匹配应用场景
在化工、石油、煤矿等存在易燃易爆气体或粉尘的危险环境中,通风设备的安全性能至关重要,防爆风扇成为保障生产环境安全与空气流通的关键装备。正确选型是确保其发挥最佳效能的前提,而根据风量风压匹配应用场景则是选型的核心要点。 风量&am…...
Laravel 中使用 JWT 作用户登录,身份认证
什么是JWT: JWT 全名 JSON Web Token,是一种开放标准 (RFC 7519)。 用于在网络应用环境间安全地传输信息作为 JSON 对象。 它是一种轻量级的认证和授权机制,特别适合分布式系统的身份验证。 核心特点 紧凑格式:体积小&#x…...
Git安装
1、 下载Git https://git-scm.com/ 2、 双击【Git-2.44.0-64-bit.exe】安装: 2-1、 选择自定义安装目录:F:\software\Git 2-2、 一直点击next,直到安装成功。 2-3、 在git项目文件夹,右键出现Git GUI Here和Git Bash Here就说明成…...
bit与byte的区别与联系?
李升伟 整理 byte 和 bit 是计算机中常用的数据单位,它们的主要区别和联系如下: 1. 定义 bit(比特):计算机中最小的数据单位,表示一个二进制位,值为0或1。 byte(字节)…...
程序化广告行业(51/89):Cookie映射与移动设备ID映射解析
程序化广告行业(51/89):Cookie映射与移动设备ID映射解析 在当今数字化营销的浪潮中,程序化广告已经成为企业精准触达目标客户的重要手段。作为一名对程序化广告充满兴趣的学习者,我希望通过这篇博客和大家一起深入探索…...
从吉卜力漫画到艺术创造:GPT-4o多种风格绘图Prompt大全
在3月底,GPT-4o掀起了一阵吉卜力绘图浪潮,大家纷纷输入一张图片,让4o模型进行风格化迁移,其中吉卜力风格的漫画在社交媒体上最为火热。在大家争议4o的训练数据是否侵权和4o背后的技术原理的时候,我们先来玩一玩&#x…...
48. 旋转图像
leetcode Hot 100系列 文章目录 一、核心操作二、外层配合操作三、核心模式代码总结 一、核心操作 先上下翻转再沿着对角线翻转 提示:小白个人理解,如有错误敬请谅解! 二、外层配合操作 三、核心模式代码 代码如下: class S…...
【Linux篇】自主Shell命令行解释器
📌 个人主页: 孙同学_ 🔧 文章专栏:Liunx 💡 关注我,分享经验,助你少走弯路! 文章目录 1. 获取用户名的接口2. 等待用户输入接口3. 将上述代码进行面向对象式的封装4. 命令行解析5.…...
leetcode 2873. 有序三元组中的最大值 I
欢迎关注更多精彩 关注我,学习常用算法与数据结构,一题多解,降维打击。 文章目录 题目描述题目剖析&信息挖掘解题思路方法一 暴力枚举法思路注意复杂度代码实现 方法二 公式拆分动态规划思路注意复杂度代码实现 题目描述 [2873] 有序三元…...
深度学习 Deep Learning 第14章 自编码器
深度学习 Deep Learning 第14章 自编码器 内容概要 本章深入探讨了自编码器(Autoencoders),这是一种用于特征学习和降维的神经网络架构。自编码器通过编码器和解码器两个部分,将输入数据映射到一个内部表示(编码&…...
全国产传感器的可靠性、MTBF计算、极限测试与加速寿命测试
全国产传感器的可靠性是指传感器在规定条件下和规定时间内完成规定功能的能力。它是衡量传感器性能的重要指标之一,直接影响传感器的使用寿命和系统稳定性。武汉利又德的小编来和大家分享一下关于全国产传感器的可靠性以及MTBF计算、极限测试与加速寿命测试的小知识…...
【算法中的数学】裴蜀定理(Bézout’s Identity)总结
裴蜀定理(Bzout’s Identity)总结 裴蜀定理是数论中的一个重要定理,描述了整数线性组合与最大公约数(GCD)之间的关系。 1. 裴蜀定理的内容 对于任意两个整数 a a a 和 b b b,设它们的最大公约数为 d …...
unity点击button后不松开通过拖拽显示模型松开后模型实例化
using System.Collections; using UnityEngine; using UnityEngine.EventSystems; using UnityEngine.UI;[RequireComponent(typeof(Button))] // 确保脚本挂在Button上 public class DragButtonSpawner : MonoBehaviour, IPointerDownHandler, IDragHandler, IPointerUpHandle…...
计算机网络复习 吉林大学
1、信息交换的三种方式:电路交换,分组交换,报文交换。 从通信资源的分配角度来看,交换就是按照某种方式动态地分配传输线路的资源。 电路交换:(星形结构替代全连接) 电话交换机接通电话的方式…...
超级好用的小软件,连接电脑和手机。
将手机变成电脑摄像头的高效工具Iriun Webcam是一款多平台软件,能够将手机摄像头变成电脑的摄像头,通过简单的设置即可实现视频会议、直播、录制等功能。它支持Windows、Mac和Linux系统,同时兼容iOS和Android手机,操作简单&#x…...
【JavaScript】十三、事件监听与事件类型
文章目录 1、事件监听1.1 案例:击关闭顶部广告1.2 案例:随机点名1.3 事件监听的版本 2、事件类型2.1 鼠标事件2.1.1 语法2.1.2 案例:轮播图主动切换 2.2 焦点事件2.2.1 语法2.2.2 案例:模拟小米搜索框 2.3 键盘事件2.3.1 语法2.3.…...
微服务架构技术栈选型避坑指南:10大核心要素深度拆解
微服务架构的技术栈选型直接影响系统的稳定性、扩展性和可维护性。以下从10大核心要素出发,结合主流技术方案对比、兼容性评估、失败案例及优化策略,提供系统性选型指南。 1. 服务框架与通信 关键考量点 扩展性:框架需支持定制化扩展&#x…...
虚拟试衣间微信小程序解决方案
目录 项目名称: 云尚衣橱 核心功能模块: 技术栈选型: 架构设计概览: 详细功能点实现思路: 数据库设计 (MongoDB 示例): 开发步骤建议: 关键注意事项和挑战: 项目名称: 云尚衣橱 核心功能模块: 用户系统 (User System) 我的衣柜 (My Wardrobe) 虚拟试衣间 (Vir…...
C++STL——容器-vector(含部分模拟实现,即地层实现原理)(含迭代器失效问题)
目录 容器——vector 1.构造 模拟实现 2.迭代器 模拟实现: 编辑 3.容量 模拟实现: 4.元素的访问 模拟实现 5.元素的增删查改 迭代器失效问题: 思考问题 【注】:这里的模拟实现所写的参数以及返回值,都是…...
MoLe-VLA:通过混合层实现的动态跳层视觉-语言-动作模型实现高效机器人操作
25年3月来自南京大学、香港理工、北大和香港科技大学的论文“MoLe-VLA: Dynamic Layer-skipping Vision Language Action Model via Mixture-of-Layers for Efficient Robot Manipulation”。 多模态大语言模型 (MLLM) 在理解复杂语言和视觉数据方面表现出色,使通用…...
《数字图像处理》教材寻找合作者
Rafael Gonzalez和Richard Woods所著的《数字图像处理》关于滤波器的部分几乎全错,完全从零开始写,困难重重。关于他的问题已经描述在《数字图像处理(面向新工科的电工电子信息基础课程系列教材)》。 现寻找能够共同讨论、切磋、…...
uni-app 框架 调用蓝牙,获取 iBeacon 定位信标的数据,实现室内定位场景
背景:最近需要对接了一个 叫 iBeacon 定位信标 硬件设备,这个设备主要的作用是,在信号不好的地方,或者室内实现定位,准确的找到某个东西。就比如 地下停车场,商城里,我们想知道这个停车场的某个…...
Java面试黄金宝典29
1. 什么是普通索引和唯一性索引 定义: 普通索引:是最基本的索引类型,它为数据表中的某一列或多列建立索引,以加快数据的查询速度。它不限制索引列的值重复,允许存在多个相同的值。唯一性索引:在普通索引的基…...
C语言常见3种排序
主要是三种排序方法:冒泡排序、选择排序、插入排序。 文章目录 一、冒泡排序 1.代码: 2.工作原理: 3.具体过程: 二、选择排序 1.代码 2. 工作原理 3.具体过程: 三、插入排序 1.代码 2.工作原理 3.具体过程 总结 一、…...