在第2节实践了从金融数据接口包例如tushare.pro或akshare获取数据,在第3节实践了直接从网页上爬取股票数据。其实,我们的电脑上怎么可能没有几个股票软件,在这些股票软件里,历史行情,实时行情都有,我们能否把这么数据提取出来?
那么这一节,我们就实践一下从股票软件通达信获取数据。
我们会分成3个部分来分别实践,
- 用python读取二进制.day文件
- 用pytdx库读取
- 通达信导出数据及处理
01_用python读取二进制.day文件
参考文档:
- Python量化交易学习笔记(46)通达信日线数据获取_如何读取通达信日线数据-CSDN博客
- 通达信行情数据获取--python_股票量化交易-获取数据的两种方法-CSDN博客
- python 通达信数据_Python读取通达信本地数据-CSDN博客
001_通达信下载及文档路径
现在通达信免费版,盘后数据下载在右上角“选项”中,点击进入选择日期跨度开始下载,这个下载速度不会很快,8000多支股票/指数数据大概需要6~7分钟,下载要有点耐心。
数据下载后,根据上面参考文档1的说明,这些数据保存在"通达信安装路径\vipdoc\"下,于是我们可以直接问AI: 请帮我解析一下通达信vipdoc文件夹下的路径文件内容
通达信的
vipdoc
文件夹下存放的是股票市场的历史数据文件,这些文件通常按照市场、股票代码、文件类型等方式组织。例如,sh000001.day
文件就是上证指数的日线数据文件。通达信的数据文件格式通常是二进制格式,而不是文本格式。
于是我们可以进入vipdoc文件夹,它里面有6个子夹,其中sh和sz就是上证和深证,再下一层的lday文件夹下就是xxx.day文件了,到这里我们下载了数据并知道数据保存的路径,下一步打开文件进行数据解析。
002_二进制文件打开与解析
A_python读取二进制文件基础
在Python中读取二进制文件(如图像、音频文件等)可以使用内置的open()
函数,并指定模式为'rb'
(读取二进制文件)。
with open('example.bin', 'rb') as f:
data = f.read() # 读取整个文件
# 或者使用 f.read(n) 来读取 n 字节
# 或者逐行读取: for line in f:
要解析几个字节并将其解释为数字,你需要根据这几个字节代表的数据类型以及它们的字节序(endianness)来处理。在Python中,你可以使用struct
模块来帮助进行这种转换。
在Python的
struct
模块中,不同的字母代表不同的二进制格式。以下是常用的一些格式字符及其含义:
I
: 无符号整型(unsigned integer),4个字节。i
: 带符号整型(signed integer),4个字节。f
: 单精度浮点数(single-precision float),4个字节。h
: 短整型(short),2个字节。H
: 无符号短整型(unsigned short),2个字节。b
: 字节(byte),1个字节。B
: 无符号字节(unsigned byte),1个字节。l
: 长整型(long),4个字节(在某些平台上可能是8个字节)。L
: 无符号长整型(unsigned long),4个字节(在某些平台上可能是8个字节)。q
: 长长整型(long long),8个字节。Q
: 无符号长长整型(unsigned long long),8个字节。d
: 双精度浮点数(double),8个字节。
示例代码:
import struct
binary_data = b'\x00\x00\x00\x01\x00\x00\x80\x00\x00' # 示例二进制数据
# 解析一个4字节的无符号整数
unsigned_int = struct.unpack('>I', binary_data[:4])[0] # >表示大端字节序
print(unsigned_int) # 输出 1
int2 = struct.unpack('II', binary_data[:8]) # 默认小端字节序
print(int2)
int3 = struct.unpack('<II',binary_data[:8]) # <表示小端字节序
print(int3)
----------------------
1
(16777216, 8388608)
(16777216, 8388608)
B_读取通达信.day文件
复习了python读取二进制文件基础后,我们就可以自己动手来解析一下.day文件了。首先,我们可以用文本编辑器查看一下sh000001.day的十六进制数据:
76 7A 33 01 B0 9E 04 00 D8 DF 04 00 55 9B 04 00
71 D2 04 00 CE 18 C5 52 29 F2 1D 1A 1C 01 10 03
7B 7A 33 01 3A CE 04 00 DE E8 04 00 C6 AD 04 00
4A B3 04 00 6D F3 8C 52 92 BE AB 11 EB 02 3F 01
7C 7A 33 01 24 A9 04 00 1F D8 04 00 38 98 04 00
75 D6 04 00 23 C9 75 52 A9 54 39 0F 00 04 38 00
7D 7A 33 01 2F DB 04 00 2A F8 04 00 9A D4 04 00
D5 F2 04 00 6A 50 C0 52 EA 0C 5F 16 0D 04 2F 00
7E 7A 33 01 4F DE 04 00 E8 F2 04 00 C2 D9 04 00
2D E1 04 00 D9 80 8B 52 BF A4 49 10 C0 00 78 03
7F 7A 33 01 E4 DD 04 00 48 EB 04 00 B9 D3 04 00
17 E2 04 00 F0 68 6B 52 FE 7A 62 0D BC 02 6E 01
将最前面的4个字节进行十六进制转十进制,按照默认的小端字节序则可以得到十进制数20150902,这个数除以1000得到年份2015,其他处理可以得到9月和2日。
d1 = 0x767A3301 # 大端字节序
d2 = 0x01337A76 # 小端字节序
d1,d2
--------------------
(1987719937, 20150902)
由于这组数据取出来,后续也是要做成backtrader需要的datetime类型的index,所以这里就不去处理年,月,日,而是直接用转成datetime类型
from datetime import datetime
d2_str = str(d2)
date0 = datetime.strptime(d2_str, "%Y%m%d")
date0
-----------------
datetime.datetime(2015, 9, 2, 0, 0)
按照参数文档1的说明:
day文件中,每32个字节存储了一根日线数据,各字节存储数据如下:
00 ~ 03 字节:年月日, 整型
04 ~ 07 字节:开盘价*100,整型
08 ~ 11 字节:最高价*100,整型
12 ~ 15 字节:最低价*100,整型
16 ~ 19 字节:收盘价*100,整型
20 ~ 23 字节:成交额(元),float型
24 ~ 27 字节:成交量(股),整型
28 ~ 31 字节:(保留)
每32个字节存储了一根日线数据,结构是("IIIIIfII"),于是可以用struct直接进行解析。日期我们还是直接转为datetime类型,开、高、收、低需要除以100,成交额换算成"亿元",成交量不动,保留字节直接去掉不用。
fname = 'TDX_data/sh000001.day'
with open(fname, 'rb') as f:
buf = f.read() # 读取全部字节
buf_size = len(buf) # 总字节数
icnt=int(buf_size/32) # 数据组计数
list1 = [] # 最终list保存解析后的数据
for i in range(icnt):
out1=[]
a = struct.unpack('IIIIIfII',buf[i*32:(i+1)*32])
date0 = dt.datetime.strptime(str(a[0]), "%Y%m%d")
open1 = a[1]/100.0
high2 = a[2]/100.0
low3 = a[3]/100.0
close4 = a[4]/100.0
amount5 = a[5]/100000000.0
volume6 = a[6]
out1 = [date0,open1,high2,low3,close4,amount5,volume6]
list1.append(out1)
list1[:2] # 显示前面2组数据
---------------------------
[[datetime.datetime(2015, 9, 2, 0, 0),
3027.68,
3194.48,
3019.09,
3160.17,
4232.62355456,
438170153],
[datetime.datetime(2015, 9, 7, 0, 0),
3149.38,
3217.58,
3066.3,
3080.42,
3026.89714176,
296468114]]
接着简单做成DataFrame类型的数据就可以了,下面的数据可以直接保存到.csv文件,也可以先做成backtrader接受的格式再保存,其中成交额并不是backtrader的FeedData的必须列,可以不要。
import pandas as pd
cols = ['date','open','high','low','close','amount(亿)','volume']
df = pd.DataFrame(list1,columns=cols)
df
--------------------------
date open high low close amount(亿) volume
0 2015-09-02 3027.68 3194.48 3019.09 3160.17 4232.623555 438170153
1 2015-09-07 3149.38 3217.58 3066.30 3080.42 3026.897142 296468114
2 2015-09-08 3054.44 3174.71 3011.12 3170.45 2639.103754 255415465
3 2015-09-09 3182.55 3256.74 3165.70 3243.09 4129.914225 375327978
4 2015-09-10 3190.55 3243.28 3178.90 3197.89 2995.810796 273261759
... ... ... ... ... ... ... ...
2174 2024-08-14 2866.24 2866.24 2850.40 2850.65 2074.575503 217885170
2175 2024-08-15 2844.20 2889.09 2839.39 2877.36 2549.871084 271900000
2176 2024-08-16 2877.94 2888.68 2873.06 2879.43 2436.082729 263653441
2177 2024-08-19 2877.95 2905.15 2877.77 2893.67 2429.948887 261687730
2178 2024-08-20 2895.55 2896.52 2855.33 2866.66 2373.078057 263658002
2179 rows × 7 columns
C_通达信数据的问题
由上步骤,我们从通达信的.day文件经过处理获取到股票的历史行情数据,可以送到backtrader中进行回测了。但是不要过于乐观了,我们得到的数据有一部分跟前面金融接口包得到的其实不一样。
我们先看上证指数,2015年9月2日的开盘价是3027.68,然后进入到通达信软件中去,找到这一根K线的数据,并截图出来,我们看到数据是一一对应的,没有问题。
fname = 'TDX_data/sh000001.day'
with open(fname, 'rb') as f:
buf = f.read(32) # 只读取一根K线
a = struct.unpack('IIIIIfII',buf)
a
----------------------
(20150902, 302768, 319448, 301909, 316017, 423262355456.0, 438170153, 51380508)
然后,随机选择一支股票,顺口的代码 600600
fname2 = 'TDX_data/sh600600.day'
with open(fname2, 'rb') as f:
buf = f.read(32)
a = struct.unpack('IIIIIfII',buf)
a
-----------------------
(20150902, 3250, 3419, 3210, 3408, 342315360.0, 10166265, 65536)
我们在通达信软件里面打开K线图并找到它对应的那根K线(20150902),但是截图显示其开盘价为:24.66(不是32.50),包括高、低、收都与解析出来的数值不对应,但有趣的是成交额和成交量又是对上了的。
这时候,我们在股票软件中点击右上第二行的"复权",再选择不复权,得到的开盘价就是32.50能对应上了。见下图,左边是前复权的数据,右边是不复权的数据。
这让我想起好多年前在某软件里玩的模拟盘,有一次我重仓模拟买了一支股票,然后它除权10送10,于是第二天我的模拟盘里面,股票数量没变,股票价格变成只有原来的一半,总金额也变成原来一半,我的账户瞬间亏损,本来模拟玩得开开心心的,这么一来就不想玩了。
话说回来,如果是不复权的数据,那么数据里就可能会有断层,把它放到backtrader里去回测,就会出现各种奇异的结果,这也是为什么前面我们用akshare获取数据的时候参数里要选择"qfq"前复权的原因了。
003_通达信.day数据读取小结
- 我们用python读取二进制的方式读取.day文件
- 使用struct库对数据进行解析
- 通达信.day文件32字节一根K线,能解析出日期,开、高、低、收,成交量和成交额
- 通过简单处理换算可以制作成DataFrame格式,以便于输出给backtrader
- 但.day文件记录的是不复权的数据,直接用于backtrader会产生未知问题,不建议使用
02_使用pytdx库
参考文档:
- python 读取 通达信 数据 pytdx_python读取通达信本地数据-CSDN博客
- [Python]调用pytdx的代码示例_pytdx调用-CSDN博客
- 扩展行情 pytdx.exhq · pytdx (gitbooks.io)
首先,惯例咨询AI: 请简要介绍一下pytdx库的功能
pytdx
是一个Python库,用于从通达信(Tdx)平台获取中国股市的数据。通达信是一家为中国证券市场提供交易软件和服务的公司,其提供的数据接口被广泛应用于证券公司的交易终端中。pytdx
库使得开发者能够绕过图形用户界面直接通过Python代码访问这些数据,从而进行自动化数据分析或交易策略开发等应用。主要功能包括但不限于:
- 获取股票的基本信息,如股票代码、名称等。
- 获取实时行情数据,包括股票价格、成交量、涨跌幅等。
- 查询历史交易数据,包括日线、周线、月线级别的K线图数据。
- 获取指数信息以及相关指数成分股的信息。
- 支持多种市场类型,包括A股、B股、基金、债券等。
- 提供了对数据的解析功能,便于进一步处理和分析。
然后,到它的网页上看一下: 扩展行情 pytdx.exhq · pytdx (gitbooks.io)
从接口API的列表中,我们可以清楚了解pytdx库的主要功能,比如说还有交易相关的pytdx.trade等。而对我们目前而言,只需要实践标准行情以及数据文件读取二个部分就可以了。
001_pytdx.reader数据文件读取
学习一个库,还是要去看源码,我们可以在IDE里 From pytdx import reader,Ctrl+点击reader就能跳转到reader/__init__.py文件,这里的代码内容并不多:
from pytdx.reader.daily_bar_reader import TdxDailyBarReader, TdxFileNotFoundException, TdxNotAssignVipdocPathException
from pytdx.reader.min_bar_reader import TdxMinBarReader
from pytdx.reader.lc_min_bar_reader import TdxLCMinBarReader
from pytdx.reader.exhq_daily_bar_reader import TdxExHqDailyBarReader
from pytdx.reader.gbbq_reader import GbbqReader
from pytdx.reader.block_reader import BlockReader
from pytdx.reader.block_reader import CustomerBlockReader
from pytdx.reader.history_financial_reader import HistoryFinancialReader
__all__ = [
'TdxDailyBarReader',
'TdxFileNotFoundException',
'TdxNotAssignVipdocPathException',
'TdxMinBarReader',
'TdxLCMinBarReader',
'TdxExHqDailyBarReader',
'GbbqReader',
'BlockReader',
'CustomerBlockReader',
'HistoryFinancialReader'
]
我们把它们直接送到AI进行解析:
这段代码主要涉及从
pytdx.reader
模块导入多个类,用于处理和读取不同的股票市场数据。pytdx
是一个开源的Python库,用于从通达信(一个中国流行的股票交易平台)获取历史和实时的股票数据。以下是每个导入类的简要解析:
TdxDailyBarReader
: 用于读取每日股票数据(开盘价、最高价、最低价、收盘价、成交量等)。TdxFileNotFoundException
: 当试图读取的文件不存在时抛出的异常。TdxNotAssignVipdocPathException
: 当未指定VIP文件路径时抛出的异常,通常与数据文件的路径配置相关。TdxMinBarReader
: 用于读取分钟级别的股票数据。TdxLCMinBarReader
: 用于读取分钟级别的股票数据,可能与TdxMinBarReader
有细微的差别,具体取决于数据格式或来源。TdxExHqDailyBarReader
: 用于读取扩展的每日股票数据,可能包括更详细的市场信息。GbbqReader
: 用于读取股票的分时数据,如分笔成交数据。BlockReader
: 用于读取股票板块数据,即一组相关股票的集合。CustomerBlockReader
: 可能用于读取自定义的股票板块数据,允许用户定义自己的股票集合。HistoryFinancialReader
: 用于读取股票的财务历史数据,如收入、利润等。
根据解析的内容,我们把重点放在1和8上面,即读取每日股票数据和读取板块数据。前面使用python读取二进制.day文件,我们实践了自己通过编写代码将.day的二进制文件转成DataFrame格式的数据,而在pytdx里,封装了TdxDailyBarReader类
可以更方便直接的使用:
from pytdx.reader import TdxDailyBarReader, TdxFileNotFoundException
fname = 'TDX_data/sh000001.day'
reader = TdxDailyBarReader()
df = reader.get_df(fname)
df
--------------------------------
open high low close amount volume
date
2015-09-02 3027.68 3194.48 3019.09 3160.17 4.232624e+11 438170153.0
2015-09-07 3149.38 3217.58 3066.30 3080.42 3.026897e+11 296468114.0
2015-09-08 3054.44 3174.71 3011.12 3170.45 2.639104e+11 255415465.0
2015-09-09 3182.55 3256.74 3165.70 3243.09 4.129914e+11 375327978.0
2015-09-10 3190.55 3243.28 3178.90 3197.89 2.995811e+11 273261759.0
... ... ... ... ... ... ...
2024-08-14 2866.24 2866.24 2850.40 2850.65 2.074576e+11 217885170.0
2024-08-15 2844.20 2889.09 2839.39 2877.36 2.549871e+11 271900000.0
2024-08-16 2877.94 2888.68 2873.06 2879.43 2.436083e+11 263653441.0
2024-08-19 2877.95 2905.15 2877.77 2893.67 2.429949e+11 261687730.0
2024-08-20 2895.55 2896.52 2855.33 2866.66 2.373078e+11 263658002.0
2179 rows × 6 columns
我们看到它与前面自己写的代码读取二进制.day文件的结果几乎没有什么不同。不过可以到源码中去对比一下以发现自己的不足和别人代码的优点。
接着,我们继续实践8-板块文件读取,那么这些文件在/T002/hq_cache/下面,其实板块的文件并不多,主要就是3个,我们从源码的测试代码上只看到block_zs.dat这一个。这个文件夹下面还有很多的.dat文件,但注意是不能用blockReader来读取的,它们的格式并不相同。
fname3 = 'TDX_data/block_zs.dat' # 综合指数
fname4 = 'TDX_data/block_fg.dat' # 风格
fname5 = 'TDX_data/block_gn.dat' # 概念
from pytdx.reader import BlockReader
df = BlockReader().get_df(fname5)
df
--------------------------------
blockname block_type code_index code
0 通达信88 2 0 000100
1 通达信88 2 1 000568
2 通达信88 2 2 000625
3 通达信88 2 3 000631
4 通达信88 2 4 000708
... ... ... ... ...
29711 AI眼镜 2 72 688521
29712 AI眼镜 2 73 688525
29713 AI眼镜 2 74 688601
29714 AI眼镜 2 75 688608
29715 AI眼镜 2 76 873001
29716 rows × 4 columns
002_pytdx.hq行情
hq- 即行情的拼音首字母缩写。先看官方文档:
标准行情 pytdx.hq · pytdx (gitbooks.io)
我们需要先引入,再创建对象,然后在with语句里面,使用to_df()获取DataFrame的数据......另外参数的说明也列在了代码下面对应的位置,比如第1个参数是K线的周期是分钟还是日K等,有趣的是值为4的说明是日K线,而9的说明也是日K线,试了一下还的确都是,那为啥要2次日K线?
from pytdx.hq import TdxHq_API
api = TdxHq_API()
with api.connect('119.147.212.81', 7709):
data = api.to_df(api.get_security_bars(9, 0, '000001', 2, 10)) # 返回DataFrame
# K线:1 15分钟 2 30分钟 3 1小时 4 日K线 5 周K线 6 月K线 9 日K线 10 季K线 11 年K线
# 市场:0深圳,1上海
# 指定开始位置 0-今天, 1-昨天, 2-前天开始
# 几条记录 10-10条记录,最多800
data
-----------------------------
open close high low vol amount year month day hour minute datetime
0 10.31 10.13 10.38 10.11 1197676.0 1.221351e+09 2024 8 29 15 0 2024-08-29 15:00
1 10.11 10.16 10.26 10.11 1294059.0 1.318859e+09 2024 8 30 15 0 2024-08-30 15:00
2 10.12 10.11 10.21 10.09 968835.0 9.838988e+08 2024 9 2 15 0 2024-09-02 15:00
3 10.11 10.08 10.13 10.00 916581.0 9.208847e+08 2024 9 3 15 0 2024-09-03 15:00
4 10.05 10.02 10.15 10.00 847992.0 8.522246e+08 2024 9 4 15 0 2024-09-04 15:00
5 10.03 10.07 10.08 9.98 594208.0 5.958458e+08 2024 9 5 15 0 2024-09-05 15:00
6 10.06 10.08 10.21 10.05 878865.0 8.915078e+08 2024 9 6 15 0 2024-09-06 15:00
7 10.05 9.85 10.07 9.84 1639174.0 1.624858e+09 2024 9 9 15 0 2024-09-09 15:00
8 9.86 9.90 9.92 9.78 767901.0 7.561247e+08 2024 9 10 15 0 2024-09-10 15:00
9 9.88 9.65 9.88 9.63 1108727.0 1.078602e+09 2024 9 11 15 0 2024-09-11 15:00
在这里,有必要使用with api.conect()来建立连接,然后数据的获取需要在建立连接的基础上进行。除些之外,其他的看起来和使用akshare获取也没有太多的差别。
还是要着重说一下,关于前复权和不复权的问题,pytdx官网上有通过同花顺的爬虫获取前后复权的数据,这么看的话,本身可能还是存在这个问题的,在这样的情况下,我们还是不建议读取.day文件以及用pytdx请求历史行情数据,在第2节的tushare.pro或akshare就已经能够实现了。
003_Mootdx
参考:批量下载 - MOOTDX
这个也是从pytdx的参考文档1中无意间发现的,跳到它的官网看说明:
Mootdx 是一款纯 Python 语言开发的类似 TDX 的行情数据接口的实现。
- 在线文档: https://www.mootdx.com
- 国内镜像: mootdx: 通达信数据读取 pytdx 的一个简便使用封装
项目特点
- 基于
pytdx
二次封装。
简单看了一下,这个是基于pytdx的二次封装,好像是会把读取.day文件等变得更简单一些,以及行情数据请求更清晰,如果有兴趣,不妨实践一下,这里我们就跳过了。
03_通达信导出数据的处理
终于来到通达信导出数据这一部分。
001_TDX数据导出
我在网上搜索了很多次,没有看到有文章写这方面的内容,大概是用通达信的高手往往精于通达信的指标都是十几年以上的老股民,而做量化交易的往往直接在量化交易的软件上做,两者搭配起来的比较少的缘故吧,不过这只是自己的瞎猜而已。
通达信软件在选项菜单里有个“数据导出”项,这条指令在不同的页面上会导出不同的数据,比如
- 在自选股页面上导出的就是自选股,
- 在股票日K线页面上导出的就是日K线数据,
- 如果在15分钟页面上导出的就是15分钟的K线数据。
002_导出数据示例
我们来看一下数据导出的一些示例,之前的章节里讲过做自选股列表,那个时候还需要制作全股票列表,以及把中文名称制作成拼音的首字母缩写等等,这样才能用不同方式获取到需要的股票。而这些做法其实都是在借鉴股票软件的已有功能,我们在股票软件里可以非常轻松的做很多个自选股页(自定义板块),比如用某个选股指标(假设KDJ)选出的50支股票,马上可以放进自定义板块中,然后数据导出来进行简单处理,就变成了backtrader的需要回测的自选股列表,我们就可以非常有针对的进行回测,也能够大大提高数据分析的效率。
自选股列表
以一个简洁自选股(自定义板块)为例,在通达信里显示是这样的:
导出成xls文件显示:
我们在用pandas读取excel文件遇到了连续的问题,包括没有设置engine,以及格式不正确等:
ValueError: Excel file format cannot be determined, you must specify an engine manually.XLRDError: Unsupported format, or corrupt file: Expected BOF record; found b'\xb4\xfa\xc2\xeb\t\xc3\xfb\xb3'
在这里我们先投个降,改数据导出为.txt文件,以便于进行数据的读取和处理。然后一上来会读取报错:UnicodeDecodeError 。
fname_list2 = "C:/new_tdx/T0002/export/简洁自选股20240913.txt"
import pandas as pd
df_s_list = pd.read_table(fname_list2)
df_s_list
---------------------
UnicodeDecodeError: 'utf-8' codec can't decode byte 0xb4 in position 0: invalid start byte
这是一个典型的错误,中文版的WIN11默认编码是GBK,这个.txt文件中含有大量的中文字符,而python的默认编码是UTF-8,因此出现了 ''utf-8' codec can't decode的问题,针对这个问题,加上encoding='GBK‘ 就能解决了。
import pandas as pd
df_s_list = pd.read_table(fname_list2,encoding='GBK')
df_s_list.iloc[:,:6]
------------------------------
代码 名称(15) 涨幅% 现价 涨跌 买价
0 601168 西部矿业 0.21 14.300 0.030 14.300
1 300058 蓝色光标 -1.87 5.250 -0.100 5.250
2 000921 海信家电 -2.43 24.450 -0.610 24.450
3 601086 国芳集团 -2.19 3.570 -0.080 3.570
4 600794 保税科技 1.03 2.930 0.030 2.910
5 002516 旷达科技 0.52 3.860 0.020 3.860
6 300697 电工合金 -1.49 9.270 -0.140 9.270
7 159633 中证1000指数ETF -1.15 1.809 -0.021 1.809
8 515220 煤炭ETF -0.88 1.017 -0.009 1.017
9 560080 中药ETF -0.97 0.916 -0.009 0.915
10 159967 创业板成长ETF 1.13 0.358 0.004 0.357
11 513660 恒生ETF 0.63 2.083 0.013 2.082
12 601058 赛轮轮胎 -1.45 13.610 -0.200 13.610
13 512880 证券ETF 0.25 0.805 0.002 0.804
14 512690 酒ETF -2.39 0.490 -0.012 0.489
15 数据来源:通达信 NaN NaN NaN NaN NaN
这里是索引15出现了问题,看上面的xls文件输出,因为最后一行是“数据来源:通达信”而不是有效数据,因此这个数据还要去除最后一行。而如果我们在一个UI程序里(比如wxPython),只需要少添加一行数据就可以了。
name_list = list(df_s_list.iloc[:,1])
for item in name_list[:-1]:
self.m_checkList1.Append(item)
这样,我们就能在backtrader回测窗口程序里,得到自选股列表,还可以用fileopenbox点击更换另外一个从通达信里导出的自定义板块文件,实现多个自选股列表的切换,而在对需要的股票进行回测,只需要勾选上就行。
于是我们就让通达信和backtrader回测进行了第一次比较深度的结合运用,不用为回测哪些股票,以及这些股票我当时为什么挑它们而发愁了,默认自选股就是我关注的股票/ETF,而自定义板块出来的,就是当时根据某指标/策略选到的,如果我再把导出时的文件名以某指标/策略命名加上日期,这就是比较不错的资料管理系统的雏形。
日K线数据处理
日K线需要进入K线图,如下图,窗口上所有的曲线都会以数据的形式导出,
比如这张图上添加了主图指标TDXWAVE,副图上只有一个CCI指标,导出的数据就会成为这样:0-时间,1-开,2-高,3-低,4-收,5-成交量(默认数据没有在图上显示),以及自己在通达信页面上添加的6-TDXWAVE指标和7-CCI指标。
简单说明一下,这里把窗口设成了2个,左下角"窗口"可以选择显示1-12个窗口,那么显示出来的窗口里的指标,是会都导出到文件里的。
这个文件我们也可以很轻松处理为backtrader接受的格式,甚至在excel里手动制作成.csv的文件就能让backtrader程序成功读取并完成回测,以下是函数的代码需求:
- pandas读取时需要跳过第一行,且第二行是标题
- 仍要注意最后一行是“数据来源:通达信”的说明,需要去除
- index是日期,需要转为datetime类型
- 标题是中文,需要改为英文
- 成交量是股数,一般都会除以100得到交易的“手”数
fname_kline = 'C:/new_tdx/T0002/export/515220.txt'
dfout = TDX_data_to_feed(fname_kline)
dfout
---------------------------------
open high low close volume openinterest
trade_date
2022-12-23 1.035 1.044 1.026 1.043 78252704.0 0
2022-12-26 1.038 1.049 1.036 1.049 64240300.0 0
2022-12-27 1.056 1.070 1.053 1.070 79422496.0 0
2022-12-28 1.063 1.072 1.057 1.062 67954600.0 0
2022-12-29 1.057 1.057 1.035 1.038 88724304.0 0
... ... ... ... ... ... ...
2024-09-09 1.054 1.054 1.023 1.031 106600896.0 0
2024-09-10 1.028 1.043 1.022 1.031 98271800.0 0
2024-09-11 1.013 1.025 1.004 1.016 116725800.0 0
2024-09-12 1.018 1.033 1.017 1.026 120319984.0 0
2024-09-13 1.027 1.034 1.016 1.017 109814400.0 0
420 rows × 6 columns
这里补充一下关于复权的问题,用“数据导出”得到的数据可以是不复权的,也可以是前复权甚至后复权的,这完全取决于你的通达信当前页面上"复权"选择的选项,而不会像.day文件那样一定是不复权的数据。所以这种方式会让你得到更有自由度的数据。
另外,通达信的一些特有指标是加密的,你要在backtrader里做出这些指标是非常有挑战的,但通过"数据导出",这些指标的值都是能获取的(例如上面的TDXWAVE和CCI),这样我们就不需要在backtrader里再编写那些通达信里已有的指标公式。在后续的章节里我们再来详细的进行实践。
04_小结
backtrader进行回测的数据也可以从通达信这类股票软件中获取,本节先实践了在通达信里完成"盘后数据下载"后,用python读取二进制的方式得到日K线数据;然后使用pytdx库的reader类对通达信下载的各种数据文件进行读取。最后介绍了一种非常容易完成的操作即"数据导出"来获得backtrader回测所需要的日K线数据的方法,使backtrader能与通达信深度结合运用。