几年前帮朋友写程序化交易的代码,写了一个样例作为基础,后来在此基础上做了些开发。现在把步骤简单记录一下。
首先下载CTP交易接口
https://www.citicsf.com/download/ctp/
我下载了linux版本tradeapi(V6.3.5/6.3.6)
下载后解压,得到32位和64位动态库,分别放在两个文件夹中
根据本机的操作系统,选择合适的版本,将其内的文件复制到合适的位置
比如将thostmduserapi.so和thosttraderapi.so放到lib文件夹,而*.h文件则放到include文件夹下,剩下的两个文件定义了标准错误信息,但写程序的时候用不到。
这样在Demo文件夹中就构造了如下路径,lib包含了两个*.so文件,include包含了4个*.h文件,src中则包含了样例源文件
其中conn中的文件是自动生成的。
接下来看看make文件
SRCS := $(wildcard src/*.cpp)
OBJS := $(patsubst %.cpp, %.o, $(SRCS))
CPP = g++
LIBS = $(wildcard lib/*.so)
INCS = -Iinclude
#FLAGS = -Wformat=0
FLAGS =
EXEC = run.exe
all: $(OBJS)
$(CPP) -o $(EXEC) $(OBJS) $(LIBS) $(FLAGS)
$(OBJS): %.o:%.cpp
$(CPP) -c $< -o $@ $(FLAGS) $(INCS)
.PHONY: clean
clean:
rm $(OBJS) $(EXEC)
该文件自动将src中的*.cpp文件替换为*.o文件,并作为生成目标,将lib中的*.so文件作为链接库,最终连接为可执行文件run.exe
最后看看源文件quotaapitest.cpp
#include <cstdio>
#include <cstdlib>
#include <cstring>
#include <semaphore.h>
#include "ThostFtdcMdApi.h"
char* FrontAddress = "tcp://ctp1-md1.citicsf.com:41213";
//char* FrontAddress = "tcp://asp-sim2-md1.financial-trading-platform.com:26213";
char* BrokerID = "6666"; // 中信期货的平台ID
//char* BrokerID = "2030";
char* InvestorID = "000023"; // 申请到的投资者账号
char* Password = "88888888"; // 设置的投资者密码
sem_t g_hEvent;
TThostFtdcBrokerIDType g_chBrokerID;
TThostFtdcUserIDType g_chUserID;
class CSimpleHandler : public CThostFtdcMdSpi
{
public:
CSimpleHandler(CThostFtdcMdApi *pUserApi) :
m_pUserApi(pUserApi), requestID(0) {}
~CSimpleHandler() {}
virtual void OnFrontConnected()
{
printf("OnFrontConnected!\n");
CThostFtdcReqUserLoginField reqUserLogin;
//printf("BrokerID:");
//scanf("%s", &g_chBrokerID);
strcpy(reqUserLogin.BrokerID, BrokerID);
//printf("userid:");
//scanf("%s", &g_chUserID);
strcpy(reqUserLogin.UserID, InvestorID);
//printf("password:");
//scanf("%s", &reqUserLogin.Password);
strcpy(reqUserLogin.Password, Password);
int ret = m_pUserApi->ReqUserLogin(&reqUserLogin, requestID++);
}
virtual void OnFrontDisconnected(int nReason)
{
printf("OnFrontDisconnected.\n");
}
virtual void OnRspUserLogin(CThostFtdcRspUserLoginField *pRspUserLogin,
CThostFtdcRspInfoField *pRspInfo, int nRequestID, bool bIsLast)
{
printf("OnRspUserLogin:\n");
printf("ErrorCode = [%d], ErrorMsg = [%s]\n", pRspInfo->ErrorID,
pRspInfo->ErrorMsg);
printf("RequestID = [%d], Chain = [%d]\n", nRequestID, bIsLast);
printf("SHFETime = %s\n", pRspUserLogin->SHFETime);
printf("DCETime = %s\n", pRspUserLogin->DCETime);
printf("CZCETime = %s\n", pRspUserLogin->CZCETime);
printf("FFEXTime = %s\n", pRspUserLogin->FFEXTime);
printf("INETime = %s\n", pRspUserLogin->INETime);
if(pRspInfo->ErrorID != 0)
{
printf("Failed to login, errorcode = %d errormsg = %s requestid = %d chain = %d",
pRspInfo->ErrorID, pRspInfo->ErrorMsg, nRequestID, bIsLast);
exit(-1);
}
char* Instrument[] = {"cu1608", "cu1606"}; // 注意品种的大小写
int ret1 = m_pUserApi->SubscribeMarketData(Instrument, 2);
//int ret2 = m_pUserApi->UnSubscribeMarketData(Instrument, 2);
int ret3 = m_pUserApi->SubscribeForQuoteRsp(Instrument, 2);
//int ret4 = m_pUserApi->UnSubscribeForQuoteRsp(Instrument, 2);
}
virtual void OnRspSubMarketData(CThostFtdcSpecificInstrumentField *pSpecificInstrument, CThostFtdcRspInfoField *pRspInfo, int nRequestID, bool bIsLast)
{
printf("OnRspSubMarketData:\n");
printf("ErrorCode = [%d], ErrorMsg = [%s]\n",
pRspInfo->ErrorID, pRspInfo->ErrorMsg);
printf("RequestID = [%d], Chain = [%d]\n", nRequestID, bIsLast);
printf("InstrumentID = %s\n", pSpecificInstrument->InstrumentID);
}
virtual void OnRspUnSubMarketData(CThostFtdcSpecificInstrumentField *pSpecificInstrument, CThostFtdcRspInfoField *pRspInfo, int nRequestID, bool bIsLast)
{
printf("OnRspUnSubMarketData:\n");
}
virtual void OnRspSubForQuoteRsp(CThostFtdcSpecificInstrumentField *pSpecificInstrument, CThostFtdcRspInfoField *pRspInfo, int nRequestID, bool bIsLast)
{
printf("OnRspSubForQuoteRsp:\n");
printf("ErrorCode = [%d], ErrorMsg = [%s]\n",
pRspInfo->ErrorID, pRspInfo->ErrorMsg);
printf("RequestID = [%d], Chain = [%d]\n", nRequestID, bIsLast);
printf("InstrumentID = %s\n", pSpecificInstrument->InstrumentID);
}
virtual void OnRspUnSubForQuoteRsp(CThostFtdcSpecificInstrumentField *pSpecificInstrument, CThostFtdcRspInfoField *pRspInfo, int nRequestID, bool bIsLast)
{
printf("OnRspUnSubForQuoteRsp:\n");
}
virtual void OnRtnForQuoteRsp(CThostFtdcForQuoteRspField *pForQuoteRsp)
{
printf("OnRtnForQuoteRsp:\n");
}
virtual void OnRtnDepthMarketData(CThostFtdcDepthMarketDataField *pDepthMarketData)
{
printf("Info: Received market data: %s\n Info: Last price: %g\n Info: Update time: %s\n",
pDepthMarketData->InstrumentID, pDepthMarketData->LastPrice, pDepthMarketData->UpdateTime);
sem_post(&g_hEvent);
}
virtual void OnRspError(CThostFtdcRspInfoField *pRspInfo, int nRequestID, bool bIsLast)
{
printf("OnRspError:\n");
printf("ErrorCode = [%d], ErrorMsg[%s]\n",
pRspInfo->ErrorID, pRspInfo->ErrorMsg);
printf("RequestID = [%d], Chain = [%d]\n", nRequestID, bIsLast);
}
virtual void OnHeartBeatWarning(int nTimeLapse)
{
printf("time out: %d\n", nTimeLapse);
}
private:
CThostFtdcMdApi *m_pUserApi;
int requestID;
};
int main()
{
sem_init(&g_hEvent, 0, 0);
CThostFtdcMdApi* pUserApi = CThostFtdcMdApi::CreateFtdcMdApi("./conn/", false);
CSimpleHandler sh(pUserApi);
pUserApi->RegisterSpi(&sh);
//pUserApi->SubscribePublicTopic(THOST_TERT_RESTART);
//pUserApi->SubscribePrivateTopic(THOST_TERT_RESTART);
pUserApi->RegisterFront(FrontAddress);
pUserApi->Init();
printf("date: %s\n", pUserApi->GetTradingDay());
printf("vertion: %s\n", pUserApi->GetApiVersion());
sem_wait(&g_hEvent);
pUserApi->Join();
pUserApi->Release();
return 0;
}
时间有点久了,需要修改一下品种的代码,但基本流程都列出了,函数名意义也比较清晰,在此做个记录,有空再整理一下。(如果运行时报错,可能需要把lib目录设置为LD_LIBRARY_PATH的值,即在Demo目录中执行export LD_LIBRARY_PATH="lib:$LD_LIBRARY_PATH"后再运行run.exe)