细数一下,自己尝试写基于CTP的程序化交易软件有以下:
1、第一次,是基于控制台的,就是网上找回来的别人写的demo,再修修改改而成的。
2、第二次,基于前面的版本,做了个有界面的,当然,这界面只是为了输出一些数据更容易查看,控制台的输出是怎样的,大学应该都知道的。
3、第三次,基于第一次的版本,做成Linux平台版本
4、第四次,基于第二次版本,做成了一个只记录行情数据的DataRecorder,尝试把行情分离出来
5、第五次,基于第四次和第二次,做成了有界面的,但行情数据没有分离的一个集合,而且添加了画K线,画指标的显示。
6、现在又有新想法,准备开第六次尝试,在尝试之前,写个文章,理顺一下思路
首先,展示一下第五次版本的样子
https://www.bilibili.com/video/BV1WB4y1w71g/
然后,第五次遇到的问题:
1,指标TR和ATR的计算公式算出来的值与TB内部的函数算出来的值有区别,这个问题还在作对比
2,CTP行情登录之后,前面5分钟内是可以获取到行情数据的,但这分钟之后,行情就没有了,就算是在OnRtnDepthMarketData这回调函数上加断点也进不到来,所以数据就没有更新了,这个问题排查了一个多星期,还没有结果,找个行情服务器IP也是不行,现在还没有解决。但是这个问题也暴露了一个问题,如果软件把行情放在一起了,那么,假如行情服务器出了问题,那么,程序有可能就要重启,那么重启这段时间的行情数据将会丢失,那么,重启后,K线数据会变形,策略信号等都一定有问题,所以这里提示我,一定要把行情另外做起来,而不能嵌入到交易程序中。另外,行情服务器最好还是多启用几个服务器,以防止一个服务器不行了,还有后备的,当然,最好还是多个期货公司提供的行情服务器。
所以第六次尝试,首先,把行情服务分离出来,然后通过网络或本地进程通信来传输行情数据。如果通过网络的话,那么,为了提高速度,行情服务器只合成K线数据(1分钟单一周期,不计算指标),尽可能的让行情服务器单纯一点。而客户端再负责把1分钟周期数据合成其它周期数据,并计算需要的指标。
目前在尝试的代码:
行情服务端:
#include <WinSock2.h>
#include <stdio.h>
SOCKET sockcli[10]; //全局变量,启动时,自动设置为0
SOCKET socketConnected[10]; //全局变量,启动时,自动设置为0
DWORD WINAPI RefeshData(LPVOID)
{
int i = 0;
char buff[100];
//实验证明,如果客户端窗口关闭了,服务器端还是照样发信息,而且不会自动停下来
while (true)
{
while (i < 10)
{
//向客户端发信息
if (sockcli[i] > 0)
{
sprintf_s(buff, "This is the %d time to send sockcli[%d]!\n",i,i);
send(sockcli[i], buff, strlen(buff), 0);
printf(buff);
memset(buff, 0, sizeof(buff));
}
i++;
}
i = 0;
Sleep(1000);
}
return 0;
}
void ReFillData(SOCKET socketClient)
{
//逐个合约逐个分钟Bar传送出去
int i = 0;
while (!strcmp("afc",""))
{
int SizeVec = 0;
}
}
void AddClient(SOCKET socketClient)
{
}
void main()
{
WORD wVersionRequested;
WSAData wsaData;
int err;
wVersionRequested = MAKEWORD(1, 1);
err = WSAStartup(wVersionRequested, &wsaData); //加载Winsock库
if (err != 0)
{
return;
}
if (LOBYTE(wsaData.wVersion) != 1 || \
HIBYTE(wsaData.wVersion) != 1)
{
WSACleanup(); //终止Winsock库的使用
return;
}
//创建套接字
SOCKET sockSrv = socket(AF_INET, SOCK_STREAM, 0);
//绑定套接字到IP端口
sockaddr_in addrSrv;
addrSrv.sin_addr.S_un.S_addr = htonl(INADDR_ANY);
addrSrv.sin_family = AF_INET;
addrSrv.sin_port = htons(9527);
if (SOCKET_ERROR == bind(sockSrv, (SOCKADDR*)&addrSrv, sizeof(sockaddr_in)) )
{
printf("bind socket failed. err == %d\n", ::GetLastError());
closesocket(sockSrv);
WSACleanup();
return;
}
listen(sockSrv, 3); //第二参数表示最大等待请求队列的大小
sockaddr_in addrCli;
int len = sizeof(sockaddr_in);
printf("This is before accept!\n");
int i = 0;
while (true)
{
if (i >= 10)
{
break; //目前调试时使用最大客户端数量为10
}
sockcli[i] = accept(sockSrv, (SOCKADDR*)&addrCli, &len); //等待连接
if (sockcli[i] != SOCKET_ERROR)
{
ReFillData(sockcli[i]);
AddClient(sockcli[i]);
if (i == 0)
{
//只在第一次成功时才启动子线程
CreateThread(NULL,0,RefeshData, NULL,0,NULL); //定时发送最新行情
}
}
i++;
}
}
客户端:
#include <WinSock2.h>
#include <stdio.h>
#include "SelfDefine/CustomStructType.h"
void main()
{
WORD wVersionRequested;
WSAData wsaData;
int err;
wVersionRequested = MAKEWORD(1, 1);
err = WSAStartup(wVersionRequested, &wsaData); //加载Winsock库
if (err != 0)
{
return;
}
if (LOBYTE(wsaData.wVersion) != 1 || \
HIBYTE(wsaData.wVersion) != 1)
{
WSACleanup(); //终止Winsock库的使用
return;
}
//创建套接字
SOCKET sockSrv = socket(AF_INET, SOCK_STREAM, 0);
//connect函数需要服务器端的信息
sockaddr_in addrSrv;
addrSrv.sin_addr.S_un.S_addr = inet_addr("127.0.0.1"); //inet_addr函数可以将点分十进制的IP字符串转成u long类型
addrSrv.sin_family = AF_INET;
addrSrv.sin_port = htons(9527);
//连接服务器
if (SOCKET_ERROR == connect(sockSrv, (SOCKADDR*)&addrSrv, sizeof(sockaddr_in)))
{
//连接错误
}
//现在以单一合约为例
while (true)
{
BarData bar = { 0 };
if (SOCKET_ERROR != recv(sockSrv, (char*)&bar, sizeof(BarData), 0))
{//如果接收没问题,则处理数据
}
}
closesocket(sockSrv);
WSACleanup();
}
以上代码都是不完整的,现在只是大概的框架,因为是自己用的,所以性能上没有作过多要求。