1.0行情读取入门

本系列是程序化交易与实现的入门。
本文主要实现了通过CTP接口读取期货行情数据,实现的语言是C++,IDE是VS2017.
需要的头问件、库文件均来自CTP接口的公开文件ThostTradeApi。
Api文档来自于公开的CTP接口文档。

新建项目——Windows控制台应用程序
在vs2017中,stdafx.h变成了pch.h文件,这对于程序的运行没有影响。
首先新建头文件Spi.h,用于继承ThostFtdcMdApi.h中的CThostFtdMdSpi函数。同时,定义一系列虚函数,用于连接行情服务器、订阅行情以及获得市场深度数据。此外,还定义了时间戳类TimeStamp,在存储深度行情序列的时候,可以用来标明时间戳。

#pragma once
#include <string.h>
#include ".\ThostFtdcMdApi.h"
#include ".\ThostFtdcUserApiStruct.h"
//继承CThostFtdcMdSpi, 此处先定义虚函数, 在Spi.cpp中实现
class CMdSpi : public CThostFtdcMdSpi
{
public:
	//错误应答
	virtual void OnRspError(CThostFtdcRspInfoField *pRspInfo, int nRequestID, bool bIsLast);
	//当客户端与交易后台通信连接被断开后,该方法被调用。发生此情况后,API会自动重新连接
	virtual void OnFrontDisconnected(int Reason);
	void OnHeartBeatWarning(int nTimeLapse);
	//心跳超时警告。当长时间未接收到报文时,调用此方法
	virtual void OnFrontConnected();
	//登录请求响应
	virtual void OnRspUserLogin(CThostFtdcRspUserLoginField *pRspUserLogin, CThostFtdcRspInfoField *pRspInfo, int nRequestID, bool bIsLast);
	//订阅行情应答
	virtual void OnRspSubMarketData(CThostFtdcSpecificInstrumentField *pSpecificInstrument, CThostFtdcRspInfoField *pRspInfo, int nRequestID, bool bIsLast);
	//取消订阅行情应答
	virtual void OnRspUnSubMarketData(CThostFtdcSpecificInstrumentField *pSpecificInstrument, CThostFtdcRspInfoField *pRspInfo, int nRequestID, bool bIsLast);
	//深度行情通知
	virtual void OnRtnDepthMarketData(CThostFtdcDepthMarketDataField *pDepthMarketData);

private:
	void ReqUserLogin();
	void SubscribeMarketData();
	bool IsErrorRspInfo(CThostFtdcRspInfoField *pRspInfo);
};

//时间戳类, 用于存储返回的时间
class TimeStamp {
public:
	//在实际返回的数据中,ts的的是类似于11:02:13这样的时间格式UpdateTime, 而mesc只会取两个值0或者500的UpdateMillic
	char ts[9];
	int mesc;
	char help[7];
	TimeStamp(char* ts, int mesc) {
		strcpy_s(this->ts, 9, ts);
		int j = 0;
		for (int i = 0; i < 9; i++) {
			if (ts[i] == ':') {
				continue;
			}
			else {
				help[j] = ts[i];
				j++;
			}
		}
		if (mesc == 0) {
			help[6] = '0';
		}
		else {
			help[6] = '5';
		}
		this->mesc = mesc;
	};

	bool operator < (const TimeStamp &other) const {
		//strcmp(str1, str2), 比较两个字符串, str1=str2: 返回0; str1>str2: 返回正数; str1<str2: 返回负数.
		//即两个字符串自左向右逐个字符比较(按ASCII值大小进行比较), 直到遇见不同的字符或者遇见'\0'为止.
		int c = strcmp(other.ts, ts);
		//当other.ts>ts时, 也就是str1表示的时间在str2后面, 比str2晚, 那么c>0
		//而实际比较中, ts1 < ts2, 表示左边的ts1早于ts2, 因为按照逻辑, ts2才是other.ts
		if (c > 0) {
			return true;
		}
		//如果秒数相同, 那么也是str1的最后修改毫秒数更大的话, 就返回true, 那么也就是str1代表更迟的时刻, 就返回true
		else if (c == 0 && other.mesc > mesc) {
			return true;
		}
		//如果完全相同, 或者str2 > str1, 那么就返回false
		return false;
	}
};

有了头文件之后,可以开始定义具体的执行程序Spi.cpp,在其中将具体定义了头文件中的虚函数。

#include "pch.h"
#include "Spi.h"
#include <iostream>
#include ".\ThostFtdcMdApi.h"
#include <vector>
#include <map>
using namespace std;
//User_Api的配置参数
extern CThostFtdcMdApi *pUserApi;
//配置参数
extern char FRONT_ADDR[];
extern TThostFtdcBrokerIDType BROKER_ID;
extern TThostFtdcInvestorIDType INVESTOR_ID;
extern TThostFtdcPasswordType PASSWORD;
//这是一个指向字符型的指针数组, 它的长度是可变的
extern const char* ppInstrumentID[];
extern int iInstrumentID;
extern map<string, CThostFtdcDepthMarketDataField> g_mapQuotes;
//设置一个timeStamp, 可以依照InstrumentID+timeStamp查找到某一合约某一时刻的数据.
//设置vector, 存储深度行情数据
//设置TimeStamp, 存储时间数据
extern TimeStamp ts;
//extern vector<CThostFtdcDepthMarketDataField*> series;
//extern map<int, map<TimeStamp, vector<CThostFtdcDepthMarketDataField>>> instrument_depth_data;
//请求编号
extern int iRequestID;

bool CMdSpi::IsErrorRspInfo(CThostFtdcRspInfoField *pRspInfo)
{
	//如果ErrorID != 0, 说明收到了错误的响应
	bool bResult = ((pRspInfo) && (pRspInfo->ErrorID != 0));
	if (bResult)
	{
		cerr << "-->>ErrorID = " << pRspInfo->ErrorID << ", ErrorMsg =" << pRspInfo->ErrorMsg << endl;
	}
	return bResult;
}

void CMdSpi::OnRspError(CThostFtdcRspInfoField *pRspInfo, int nRequestID, bool bIsLast)
{
	//cout: Standard input/output stream
	//cerr: Error output stream, mainly for wrong output
	//clog: Standard error output stream
	cerr << "-- >>> " << __FUNCTION__ << endl;
	IsErrorRspInfo(pRspInfo);
}


void CMdSpi::OnFrontDisconnected(int nReason)
{
	cerr << "--->>> " << __FUNCTION__ << endl;
	cerr << "--->>> Reason = " << nReason << endl;
}

void CMdSpi::OnHeartBeatWarning(int nTimeLapse)
{
	cerr << "--->>> " << __FUNCTION__ << endl;
	cerr << "--->>> nTimerLapse = " << nTimeLapse << endl;
}


void CMdSpi::ReqUserLogin()
{
	CThostFtdcReqUserLoginField req;
	//memset用于将某一块内存中的内容全部设置为指定的值。
	//如将指针req指向的内存地址,其后的sizeof(req)大小都设置为0
	memset(&req, 0, sizeof(req));
	strcpy_s(req.BrokerID, BROKER_ID);
	strcpy_s(req.UserID, INVESTOR_ID);
	strcpy_s(req.Password, PASSWORD);
	int iResult = pUserApi->ReqUserLogin(&req, ++iRequestID);
	//以下用到了一种三目运算符,如: x<0?y=10:z=20, 问号前面的表达式是要被测试的表达式。
	//如果正确,就执行第一个语句, 如果错误, 就执行第二个语句
	cerr << "-->>发送用户登录请求:" << ((iRequestID == 0) ? "成功" : "失败") << endl;
}

void CMdSpi::OnFrontConnected()
{
	cerr << "--->>>" << __FUNCTION__ << endl;
	//用户登录请求
	ReqUserLogin();
}


void CMdSpi::SubscribeMarketData()
{
	int iResult = pUserApi->SubscribeMarketData(ppInstrumentID, iInstrumentID);
	cerr << "--->>> 发送行情订阅请求: " << ((iResult == 0) ? "成功" : "失败") << endl;
}

void CMdSpi::OnRspUserLogin(CThostFtdcRspUserLoginField *pRspUserLogin, CThostFtdcRspInfoField *pRspInfo, int nRequestID, bool bIsLast)
{
	cerr << "--->>> " << __FUNCTION__ << endl;
	if (bIsLast && !IsErrorRspInfo(pRspInfo))
	{
		///获取当前交易日
		cerr << "--->>> 获取当前交易日 = " << pUserApi->GetTradingDay() << endl;
		// 请求订阅行情
		SubscribeMarketData();
	}

}


void CMdSpi::OnRspSubMarketData(CThostFtdcSpecificInstrumentField *pSpecificInstrument, CThostFtdcRspInfoField *pRspInfo, int nRequestID, bool bIsLast)
{
	//cerr << __FUNCTION__ << endl;
	//cout << pSpecificInstrument->InstrumentID << endl;
	return;
}

void CMdSpi::OnRspUnSubMarketData(CThostFtdcSpecificInstrumentField *pSpecificInstrument, CThostFtdcRspInfoField *pRspInfo, int nRequestID, bool bIsLast)
{
	//cerr << __FUNCTION__ << endl;
	return;
}

void CMdSpi::OnRtnDepthMarketData(CThostFtdcDepthMarketDataField *pDepthMarketData)
{
	//map的基本功能是迭代器
	/*
	
	g_mapQuotes[pDepthMarketData->InstrumentID] = *pDepthMarketData;
	map<string, CThostFtdcDepthMarketDataField>::iterator iter;
	for (int i = 0; i < iInstrumentID; i++) :
	{
		if (pDepthMarketData->InstrumentID[0] == ppInstrumentID[i][0] &&
			pDepthMarketData->InstrumentID[1] == ppInstrumentID[i][1] &&
			strcmp(pDepthMarketData->InstrumentID, ppInstrumentID[i] != 0)
		{
			iter = g_mapQuotes.find(ppInstrumentID[i]);
			iter != g_mapQuotes.end();
				if (strcmp(pDepthMarketData->InstrumentID, ppInstrumentID[i]) > 0)
				{
					cout<<pDepthMarketData->InstrumentID<<"Last:"<<iter->first.c_str()<<"最新"<<pDepthMarketData->LastPrice
						<<iter->first.c_str()<<"卖一"<<iter.first
					cout
						<< pDepthMarketData->InstrumentID << "\t"
						<< pDepthMarketData->ClosePrice << "\t"
						<< pDepthMarketData->AskPrice1 << "\t"
						<< pDepthMarketData->AskVolume1 << "\t"
						<< pDepthMarketData->BidPrice1 << "\t"
						<< pDepthMarketData->BidVolume1 << "\t"
						<< pDepthMarketData->UpdateTime
						<< pDepthMarketData->TradingDay << endl;
				}
		}
	}

	*/
			cout
				<< pDepthMarketData->InstrumentID << "\t"
				<< pDepthMarketData->ClosePrice << "\t"
				<< pDepthMarketData->AskPrice1 << "\t"
				<< pDepthMarketData->AskVolume1 << "\t"
				<< pDepthMarketData->BidPrice1 << "\t"
				<< pDepthMarketData->BidVolume1 << "\t"
				<< pDepthMarketData->UpdateTime
				<< pDepthMarketData->TradingDay << endl;
}

头文件定义完之后,可以开始执行main函数,读取期货行情。

#include "pch.h"
#include <vector>
#include <map>
#include <iostream>
#include "Spi.h"
#include ".\ThostFtdcMdApi.h"
using namespace std;
// UserApi对象
CThostFtdcMdApi* pUserApi;
// 配置参数
char FRONT_ADDR[] = "XXXXXXX";		
TThostFtdcBrokerIDType	BROKER_ID = "XXXXX";			
TThostFtdcInvestorIDType INVESTOR_ID = "XXXXXXXXXXX";		
TThostFtdcPasswordType  PASSWORD = "XXXXXXXXXXXXXXXXXX";		
const char *ppInstrumentID[] = 
				{ "IF1903", "IF1904", "IF1906", "IF1909", 
				"IH1903", "IH1904", "IH1906", "IH1909", 
				"IC1903", "IC1904", "IC1906", "IC1909"};		
int iInstrumentID = 12;							
int iRequestID = 0;


int main()
{
	// 初始化UserApi
	pUserApi = CThostFtdcMdApi::CreateFtdcMdApi();			
	CThostFtdcMdSpi* pUserSpi = new CMdSpi();
	pUserApi->RegisterSpi(pUserSpi);						
	pUserApi->RegisterFront(FRONT_ADDR);					
	pUserApi->Init();
	pUserApi->Join();
	//	pUserApi->Release();
}

至此,已经可以读取期货行情了,本程序读取的是IF/IH/IC的1903,1904,1906,1909合约的行情数据。
结果如下:
在这里插入图片描述
因为是周末,所以没有任何数据。

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值