2009年1月29日星期四

同花顺日线以及5分钟线格式

闲来无事想弄个小分析软件自己玩玩,还没开头就发现找不到合适的股票数据接口。原来想读取招商证券的那个通达信的缓存文件 ,结果这帮子小气鬼,居然只允许下载1个月之内的5分钟线。(不要尝试着去破解客户端的限制,丫的服务器里就存了1个月的历史数据,空欢喜者上)于是,只能找同花顺大哥帮忙了。这位兄弟又有免费帐号,又提供1年多的5分钟线下载,真是够慷慨的。

同花顺的历史文件格式,网络上还没见到完整的解析文档,于是自己挽起袖子来解码了一把。本次的用的软件是同花顺2009,版本号4.50.31。

先说说日线格式:
日线文件在history下,若是上交所的,那么文件在shase\day下面;若是深交所的,文件在sznse\day下面。文件名对应了股票代码,扩展名是.day。譬如sznse\day\000002.day存的就是万科A的日线。

日线文件是一个简单的文件头+数据块结构。文件头72字节,后续的数据块每个56字节。
数据块中,日期,开盘,最高,最低,收盘,金额,数量,按照顺序各自占有4个字节,后面还有28个字节就不管啥用直接略过了。

日期是简单的32位有符号整形,读取出来就是那种20090123这样的数字,一眼就能认出来。
开盘,最高,最低,收盘,金额,数量这6块数据,就是比较麻烦的同花顺格式。这个格式是32位有符号整形的变种,到后面慢慢说 。

数据块结构如下:

日期 4字节 32位有符号整形
开盘 4字节 同花顺格式
最高 4字节 同花顺格式
最低 4字节 同花顺格式
收盘 4字节 同花顺格式
金额 4字节 同花顺格式
数量 4字节 同花顺格式
未知 28字节 略过

5分钟线文件格式:
5分钟线文件在history下,若是上交所的,那么文件在shase\min5下面;若是深交所的,文件在sznse\min5下面。文件名对应了股票代码,扩展名是.mn5。譬如sznse\min5\000002.mn5存的就是万科A的5分钟线。

5分钟线文件同样是简单的文件头+数据块结构。文件头56字节,后续的数据块每个40字节。
数据块中,日期,开盘,最高,最低,收盘,金额,数量,按照顺序各自占有4个字节,后面还有12个字节也直接略过了。
日期是32位有符号整形,基本上能肉眼认出来,举个例子:1901231400,最开头的19代表年份,1990+19 = 2009就是实际年份;0123是1月23号;1400么就是下午14:00,比日线的稍微复杂一点。
开盘,最高,最低,收盘,金额,数量这6块数据,也同日线一样,是比较麻烦的同花顺格式。

数据块结构如下:

日期 4字节 32位有符号整形
开盘 4字节 同花顺格式
最高 4字节 同花顺格式
最低 4字节 同花顺格式
收盘 4字节 同花顺格式
金额 4字节 同花顺格式
数量 4字节 同花顺格式
未知 12字节 略过

最后需要说下这个同花顺格式:
总体来说,这个表示格式是由最高位的4Bit的符号位和后面28个Bit的数值区为拼接而成。举例说,B0001B8A这个数值,最高位的B是符号位,后面的0001B8A是数值区。
符号位从0到F总共有16种数值。1-7表示缩小,9-F表示放大,缩小放大都是以数量级的形式。
举例,B0001B8A这个数值,符号位B表示存入文件的数值被放大了B-9=3,3个数量级,即1000倍。那么其实际数值为7050(0001B8A)/1000=7.05;
163A5012,符号位1表示存入文件的数值被缩小了1-0=1,1个数量级,即10倍,那么实际数值为104484882(63A5012)*10 =1044848820。最后举例,003B0201这个数值,符号位是0,那么就直接取其数值区03B0201,实际数值是3867137。

这种格式比起系统的Float(都是4个字节),在精度上要略高一筹,数值范围虽然要小很多,但是对于股票数据来说也绝对够用,的确是个不错的想法。

截取一段C#代码:
private double ConvertTHSData(Byte[] raw)
{

//若最高位的4bit大于8,则表示它代表数值被放大了多少个数量级,若小于8,则表示其是被缩小了多少个数量级,

若是0,则表示没有变化
int FlagBit = raw[3] >> 4;
double MutiBase = 1;
int NumBase = 0;
if (FlagBit >8)//大于8
{
MutiBase = Math.Pow(10, FlagBit - 8);//若是9,那么表示放大了9-8=1 ,1个数量级即10倍;若是B,那么放

大了B-8 =3,3个数量级即1000倍
NumBase = BitConverter.ToInt32(raw, 0) & 0xFFFFFFF;
}
else if (FlagBit > 0 && FlagBit <8) //在 1和8之间
{
MutiBase = 1/Math.Pow(10, FlagBit); //若是1,表示被缩小了1个数量级即是原来的1/10
NumBase = BitConverter.ToInt32(raw, 0) & 0xFFFFFFF;
}

else //等于0
{
NumBase = BitConverter.ToInt32(raw, 0);
}

return NumBase / MutiBase;
}

没有评论: