通达信插件获取并存储通达信商品指数的实时数据

一、引子

通达信商品指数一共有23个,如下图所示:

如果想获取历史数据,只需要通过通达信的数据下载和导出功能即可,现在我们需要获取这23个指数的实时数据,通过导出功能就没有办法了。

在最初的阶段,考虑的是合成的逻辑,即找到每一个指数的成份股,计算出对应的加权指数后再进行算术平均来计算对应的指数。这种方法的好处是每一样都可以算出来,缺点是总是有一点点误差,可能是商品指数的加权应该有一种修正逻辑,并且由于每次的计算都有误差,随着时日久远,误差会越来越大,造成完全不可用,后来没有办法,每天下载23个期货指数做为修正基数,勉强也用了一段时间。最近不知为何,忽然发现指数完全对不上,特别是通达信商品指数,乱的不像样,把之前的所有努力都废了。

自己计算看来没有办法了,可能是通达信中修正逻辑改了或是其它什么原因导致算不准了,只能考虑直接使用通达信的板块指数

二、pytdx

这种东西看起来可以获取到通达信的几乎所有数据,获取到

通过如下代码:

from pytdx.exhq import TdxExHq_API
from pytdx.exhq import TDXParams
api = TdxExHq_API()
with api.connect("182.175.240.157", 7727):
    df = api.to_df(api.get_markets())
    print(df)
data = api.to_df(api.get_instrument_bars(TDXParams.KLINE_TYPE_1MIN, 30, "AGL9", 0, 10))
print(data)

可以连接上期货的扩展板块,并且在交易日时也可以获取到除了商品期货指数的所有期货数据(试验了一下,今天是什么数据也获取不到,应该还是该接口对于扩展数据的不稳定支持有关)

看起来商品期货指数想通过简单的api获取是不可能了

三、通达信插件

1、通达信公式的逻辑

这一块的逻辑为针对特定数据,如代码为T开头的且必须是4个字符串的认为是商品期货指数,这样就不用所有数据都保存了,这一层过滤可在公式端进行,满足这些条件的传送对应的除T以外的代码,不满足的传递-1

T1: SUBSTR(CODE,1,1) == 'T',NODRAW;
S_CODE:= SUBSTR(CODE,2,3);
T2: STRLEN(CODE)==4, NODRAW;
S_DATE:= CON2STR(DATE+19000000,0);
S_TIME:= CON2STR(TIME,0);
T3: PERIOD==0, NODRAW;

D_CODE:=IF(T1 AND T2 AND T3, STR2CON(S_CODE), -1);

考虑到我们要存储的是分钟数据,因此至少要先传入日期和分钟,由于根据插件标准,一次最多3个入参且必须是浮点数,所以一个K线数据至少需要4个函数才能实现

MM1:TDXDLL2(1,D_CODE,STR2CON(S_DATE),STR2CON(S_TIME));
MM2:TDXDLL2(2,D_CODE,OPEN,HIGH);
MM3:TDXDLL2(3,D_CODE,LOW,CLOSE);
MM4:TDXDLL2(4,D_CODE,VOL,VOLINSTK);

这样我们在公式端就完成了一个K线的基本数据的传入了,至于为何每次都需要传入D_CODE,是为了2,3,4函数中识别是否为同一个商品指数而做。

2、通达信插件的逻辑

1)插件初始化

考虑到我们需要存储到文件中,每次打开文件写入再关闭文件可能会影响效率,可以在插件刚刚加载时将对应的23个文件全部打开

int init()
{
	table_init();
	char buf[512] = { 0 };

	for (auto it = g_tables.begin(); it != g_tables.end(); it++) {
		bool FileExist = false;
		memset(buf, 0, sizeof(buf));
		sprintf(buf, "C:/TdxFutureBk/%s_%s.csv", it->first.c_str(), it->second.c_str());
		if (!_access(buf, 0))
			FileExist = true;

		FILE *p = fopen(buf, "a");
		if (p != NULL) {
			g_fstreams[it->first] = p;
			if (!FileExist) {
				fflush(p);
			}
		}
	}

	return 0;
}

g_tables是为了后续将数据存入数据库而准备,每一个商品指数对应板块表名,这里可以不考虑

然后在动态库的入口先调用init即可

BOOL APIENTRY DllMain( HMODULE hModule,
                       DWORD  ul_reason_for_call,
                       LPVOID lpReserved
					 )
{
	switch (ul_reason_for_call)
	{
	case DLL_PROCESS_ATTACH:
		init();
		// 第一次将一个DLL映射到进程地址空间时调用
		// The DLL is being mapped into the process' address space.
		break;

2)void FutureBk1(int DataLen, float *pfOUT, float *pfINa, float *pfINb, float *pfINc)的逻辑

此函数首先需判断DataLen是否大于1,否的话退出

然后再判断pfINa是否大于0,否的话说明不是国内期货指数,也退出

接下来从INa中获取代码,与“T"拼接在一起作为指数代码code,从INb中获取日期,从INc中获取时间,

这些完成后进行全局的数据初始化

	char szTime[32] = { 0 };
	memset(szTime, 0, sizeof(szTime));
	sprintf(szTime, "%04d-%02d-%02d %02d:%02d:%02d", iDate / 10000, (iDate % 10000) / 100, 
		(iDate % 10000) % 100, iTime / 100, iTime % 100, 0);
	memset(buf, 0, sizeof(buf));
	sprintf(buf, "%s,%s,1", bkCode.c_str(), szTime);
	g_datas[code] = buf;

3)void FutureBk2,3(int DataLen, float *pfOUT, float *pfINa, float *pfINb, float *pfINc)

DataLen和pfINa的检查是与Bk1一致的

然后将INb转成open, INc转成high

然后检查code对应的g_datas不存在或是对应的值为空字符串,说明数据不合法,退出

如果通过检查,则将转换成的open,high追加到全局变量g_datas中

	memset(buf, 0, sizeof(buf));
	sprintf(buf, "%s,%s,%s", data.c_str(), open.c_str(), high.c_str());
	g_datas[code] = buf;

FutureBk3与2类似逻辑,只是追加的为low,close

4、void FutureBk4(int DataLen, float *pfOUT, float *pfINa, float *pfINb, float *pfINc)

FutureBk4与3的检查是一样的,通过后追加成交量和持仓量

然后需要通过code为K查找g_fstreams是否存在此code,存在的话调用g_fstreams将文本存入对应的文件中

至此,插件内容就写完了。

四、使用

生成的动态库TdxFutureBk.dll放置于T0002\dlls中然后绑定在2号库,然后开启通达信,将多股同列调成5*5,将调整成1分钟周期放置在那儿(在通达信公式中限定只存储1分钟板块数据),当有实时行情时,就会将所有板块自动存于相应的板块文件中(一个板块约一分钟存入8笔左右)

 

五、存储到数据库中

如果需要对数据进行实时处理,更好的办法是存于redis中或是mysql数据库中,这样就可以利用实时板块数据来计算相应的指标了

猜你喜欢

转载自blog.csdn.net/luhouxiang/article/details/128753198