跳至主要內容

XTP行情Quote-API使用示例说明

xtp_master大约 15 分钟

XTP行情Quote-API使用示例说明



一. 行情库介绍

该文档旨在帮助开发者快速使用极速交易平台XTP的行情API,文中是Quote-API接口调用示例。


1. 头文件


文件名详情
xtp_quote_api.h行情接口头文件,行情订阅接口类。
xtp_api_struct.h业务数据结构。
xtp_api_data_type.h交易和行情的数据基本类型。
xtp_api_struct_common.h定义业务公共数据结构。
xoms_api_fund_struct.h定义资金划拨数据结构。
xoms_api_struct.h定义交易所需数据结构。
xquote_api_struct.h定义行情所需数据结构。
xquote_api_rebuild_tbt_struct.h定义行情回补所需数据结构。

2. 库文件


适用系统文件名
windowsxtpquoteapi.dll xtpquoteapi.lib
linuxlibxtpquoteapi.so
macoslibxtpquoteapi.dylib

3. 接口说明

  • (1) 行情API提供了两个接口类:行情订阅类QuoteApi接口和行情回调类QuoteSpi接口。
  • (2) 行情通知是通过异步方式提供。
  • (3) 客户端应用程序可通过QuoteApi发出订阅行情请求,通过继承QuoteSpi并重写回调函数来响应后台服务,处理行情数据。
  • (4) XTP的库文件目前只支持64位的,注意创建64位的工程,并且设置64位的编译器来进行编译。

二. Quick Start

1. 创建Api实例

示例代码如下:

	XTP::API::QuoteApi* m_pQuoteApi = NULL;
	uint8_t client_id = 1;//一个进程一个client id,可在[1, 99]区间内任选,并固定下来
	std::string save_file_path = "./";//保存xtp api日志的路径,需要有可读写权限
	XTP_LOG_LEVEL log_level = XTP_LOG_LEVEL_DEBUG;//xtp api日志的输出级别,建议调试时使用debug级别,正常运行时使用info级别
	///创建QuoteApi
	m_pQuoteApi = XTP::API::QuoteApi::CreateQuoteApi(client_id, save_file_path.c_str(), log_level);
	if (NULL == m_pQuoteApi)
	{
		//创建API失败
	}

2. 初始化Api参数

(1)设定心跳超时时间

示例代码如下:

	///设定行情服务器超时时间,单位为秒,默认是15s,调试时可以设定大点
	uint32_t heat_beat_interval = 15;
	m_pQuoteApi->SetHeartBeatInterval(heat_beat_interval);

(2)设定UDP缓存buffer

示例代码如下:

	//设定UDP本地缓存buffer大小,单位为MB
	int buffer_size = 512;//2.2.30.7以上版本api,建议不超过512,最大仅支持1024
	m_pQuoteApi->SetUDPBufferSize(buffer_size);

(3)设定UDP异步输出日志

示例代码如下:

	//设定是否输出异步日志
	bool  log_output_flag = true;//刚实盘运行时,或者调试测试时,建议开启,实盘运行正常后,可以关闭
	m_pQuoteApi->SetUDPSeqLogOutPutFlag(log_output_flag);

3. 创建Spi类

如果想要获取行情数据,必须得有自己的回调响应类。下面仅以快照行情为例,创建了一个Spi回调响应类MyQuoteSpi,具体步骤如下:

(1)继承XTP::API::QuoteSpi,创建自己的Spi类MyQuoteSpi

quote_spi.h文件,行情回调类接口定义:

#include "xtp_quote_api.h"

using namespace XTP::API;

class MyQuoteSpi : public QuoteSpi
{
public:
	MyQuoteSpi();
	~MyQuoteSpi();

	///行情服务器断线通知
	void OnDisconnected(int reason);

	///订阅快照行情应答
	void OnSubMarketData(XTPST *ticker, XTPRI *error_info, bool is_last);
	
	///订阅快照行情应答
	void OnUnSubMarketData(XTPST *ticker, XTPRI *error_info, bool is_last);
	
	///查询合约完整静态信息的应答
	void OnQueryAllTickersFullInfo(XTPQFI* ticker_info, XTPRI *error_info, bool is_last);

    ///快照行情通知,包含买一卖一队列
    void OnDepthMarketData(XTPMD *market_data, int64_t bid1_qty[], int32_t bid1_count, int32_t max_bid1_count, int64_t ask1_qty[], int32_t ask1_count, int32_t max_ask1_count);

	/// 快照行情中ETF的IOPV通知
	void OnETFIOPVData(IOPV *iopv);

	//TODO:如果需要订阅其他行情,请继续重写其他行情回调函数

};

(2)重写回调函数逻辑

quote_spi.cpp文件,行情回调类接口实现:


#include "quote_spi.h"
#include <iostream>

using namespace std;

extern XTP::API::QuoteApi* m_pQuoteApi;

MyQuoteSpi::MyQuoteSpi()
{
}

MyQuoteSpi::~MyQuoteSpi()
{
}

void MyQuoteSpi::OnDisconnected(int reason)
{
	//行情服务器断线后,此函数会被调用
	//TODO:重新login,并在login成功后,再次订阅
}

void MyQuoteSpi::OnSubMarketData(XTPST *ticker, XTPRI *error_info, bool is_last)
{
	if (error_info && error_info->error_id != 0)
	{
		//订阅失败
		cout << "OnSubMarketData -----" << "error_id = " << error_info->error_id << ", error_msg = " << error_info->error_msg << endl;
		return;
	}

	//订阅成功
	//TODO:当is_last == true时,触发其他用户逻辑	
}

void MyQuoteSpi::OnUnSubMarketData(XTPST *ticker, XTPRI *error_info, bool is_last)
{
	if (error_info && error_info->error_id != 0)
	{
		//取消订阅失败
		cout << "OnUnSubMarketData -----" << "error_id = " << error_info->error_id << ", error_msg = " << error_info->error_msg << endl;
		return;
	}

	//退阅成功
	//TODO:当is_last == true时,触发其他用户逻辑	
}

void MyQuoteSpi::OnQueryAllTickersFullInfo(XTPQFI* ticker_info, XTPRI *error_info, bool is_last)
{
	if (error_info && error_info->error_id != 0)
	{
		//查询失败
		cout << "OnQueryAllTickersFullInfo -----" << "error_id = " << error_info->error_id << ", error_msg = " << error_info->error_msg << endl;
		return;
	}

	//查询成功
	//TODO:将查询结果缓存
	
	//如果是最后一个响应,通知后续逻辑
	if (is_last == true)
	{		
		//TODO:通知后续逻辑,例如可以在查询沪市结束后,再次发起查询深市的查询请求
		if (ticker_info->exchange_id == XTP_EXCHANGE_SH)
		{
			//当沪市的静态行情查询完毕后,查询深市行情静态信息
			m_pQuoteApi->QueryAllTickersFullInfo(XTP_EXCHANGE_SZ);
		}
		else
		{
			//TODO:触发后续逻辑
		}
	}
}

void MyQuoteSpi::OnDepthMarketData(XTPMD *market_data, int64_t bid1_qty[], int32_t bid1_count, int32_t max_bid1_count, int64_t ask1_qty[], int32_t ask1_count, int32_t max_ask1_count)
{
	//收到行情快照数据
	//TODO:用户处理逻辑,注意此处不能仅仅保存数据的指针,指针所指向的内存数据将在此函数return后失效
}

void  MyQuoteSpi::OnETFIOPVData(IOPV *iopv)
{
	//收到行情快照中ETF的IOPV信息
	//TODO:用户处理逻辑
}

4. 创建Spi实例

示例代码如下:

	//创建Spi实例
	MyQuoteSpi* m_pQuoteSpi = new MyQuoteSpi();

5. 注册Spi

示例代码如下:

	//注册Spi
	m_pQuoteApi->RegisterSpi(m_pQuoteSpi);

6. 登陆行情服务器

示例代码如下:

	std::string quote_server_ip = "xxx.xxx.xxx.xxx";//行情服务器ip地址
	int quote_server_port = xxx;//行情服务器端口port
	std::string quote_username = "xxxxxxxx";//行情服务器的登陆账户名
	std::string quote_password = "xxxxxx";//行情服务器的登陆密码
	XTP_PROTOCOL_TYPE protocol_type = XTP_PROTOCOL_TCP;//Level1服务器通常使用TCP,具体以运营通知为准,Level2服务器请用UDP,公网测试环境均为TCP,以实际服务器支持的类型为准
	std::string local_ip = "xxx.xxx.xxx.xxx";//本地网卡对应的ip

	int ret = m_pQuoteApi->Login(quote_server_ip.c_str(), quote_server_port, quote_username.c_str(), quote_password.c_str(), protocol_type, local_ip.c_str());
	if (0 != ret)
	{
		// 登录失败,获取错误信息
		XTPRI* error_info = m_pQuoteApi->GetApiLastError();
		std::cout << "login to server error, " << error_info->error_id << " : " << error_info->error_msg << std::endl;

		return 0;
	}

	// 登录成功
	//TODO: 用户逻辑,例如查询静态数据、订阅行情等

7. 查询行情静态信息

一次只能查单个市场,如果需要查沪深2市场,需要。
请在一个市场查询结果全部回来后再发起另一个市场的查询,否则容易引起断线。
示例代码如下:

	//查询沪市行情静态信息
	m_pQuoteApi->QueryAllTickersFullInfo(XTP_EXCHANGE_SH);

8. 订阅行情

  • 采用方式连接的行情服务器,推荐使用单订阅行情。
  • 采用方式连接的行情服务器,推荐使用进行全市场订阅。
    下面以单订阅2只股票为例,示例代码如下:
	// 申请内存
	int ticker_count = 2;//需要订阅行情的证券代码数量,可根据实际订阅需求改动
	char **ppInstrumentID = new char*[ticker_count];
	for (int i = 0; i < ticker_count; i++) 
	{
		ppInstrumentID[i] = new char[XTP_TICKER_LEN]; 
	}
	strcpy_s(ppInstrumentID[0], XTP_TICKER_LEN, "600000");
	strcpy_s(ppInstrumentID[1], XTP_TICKER_LEN, "600001");
	//订阅沪市的600000、600001两只股票快照行情 
	m_pQuoteApi->SubscribeMarketData(ppInstrumentID, ticker_count, XTP_EXCHANGE_SH);
	
	// 释放内存
	for (int i = 0; i < ticker_count; i++) {
		delete[] ppInstrumentID[i];
		ppInstrumentID[i] = NULL;
	}
	delete[] ppInstrumentID;
	ppInstrumentID = NULL;

9. 处理回调消息

  • 发起快照订阅请求后,无论是否订阅成功,Spi都将收到快照订阅响应。可根据需要重写函数逻辑。
  • 如果快照订阅成功,快照行情将通过回调函数推送给用户。此时用户可根据收到的行情数据重写程序逻辑。
  • 如果快照订阅种类为ETF,ETF的IOPV信息可以通过回调函数推送给用户。此时用户可根据收到的IOPV数据重写程序逻辑。
  • 其余逐笔和订单簿行情,可参照快照行情进行订阅和重写函数逻辑。

10. 断线重连

重写回调函数,总体思路是调用重连成功后,重新订阅快照行情。示例代码如下:

void MyQuoteSpi::OnDisconnected(int reason)
{
	//行情服务器断线后,此函数会被调用
	//TODO:重新login,并在login成功后,再次订阅

	cout << "Disconnect from quote server. " << endl;

	//重新登陆行情服务器
	int ret = -1;
	while (0 != ret)
	{
		ret = m_pQuoteApi->Login(quote_server_ip.c_str(), quote_server_port, quote_username.c_str(), quote_password.c_str(), protocol_type);
		if (0 != ret)
		{
			// 登录失败,获取错误信息
			XTPRI* error_info = m_pQuoteApi->GetApiLastError();
			cout << "login to server error, " << error_info->error_id << " : " << error_info->error_msg << endl;

			//等待10s以后再次连接,可修改此等待时间,建议不要小于3s
#ifdef _WIN32
			Sleep(10000);
#else
			sleep(10);
#endif // _WIN32	
		}
	}

	//重连成功
	cout << "login to server success. " << endl;
	//再次订阅行情快照
	subscribeMarketData();

}

三. 行情订阅需注意的问题

  • (1) Level1行情服务器通常仅支持TCP连接,请使用连接,具体以哪种方式连接请以运营通知为准。Level2行情服务器,请使用连接。公网测试环境均使用连接。
  • (2) 登录时,可以是NULL, 但不能是空串,最好传入要使用的网卡上的ip。
  • (3) 证券代码基本信息可通过查询合约的完整静态信息,并在本地建立一个证券信息索引表。为了避免因缓慢满而导致的断线,沪深交易所请分开查询,且在一个市场查询完成后,再发起查询另一个市场信息的查询请求。
  • (4) 在行情回调函数中,请尽快处理行情数据并返回,否则可能会因为接收缓存满而导致断线。
  • (5) 如果连接的是UDP行情服务器,无论是否订阅,都是行情全接收后再本地Api筛选过滤。
  • (6) 使用UDP行情服务器时,如果本地缓存满了,会引发丢包。请务必在Login之前调用设置,推荐或。
  • (7) 使用订阅/退订函数,请注意使用。不支持全订阅后,再退订部分,或者订阅部分再全退订。

四. 简单Demo示例代码

下面是一个简单的代码示例,演示了行情API通过TCP方式连接Level1行情服务器的过程,包括:创建、初始化、登录行情、查询行情静态信息、订阅行情、断线重连。
main.cpp文件

#include "xtp_quote_api.h"
#include <string>
#include <iostream>
#ifdef _WIN32
#include <windows.h>
#else
#include <unistd.h>
#endif // _WIN32
#include "quote_spi.h"

XTP::API::QuoteApi* m_pQuoteApi = NULL;//全局变量,程序共用一个api
std::string quote_server_ip = "xxx.xxx.xxx.xxx";//行情服务器ip地址
int quote_server_port = xxx;//行情服务器端口port
std::string quote_username = "xxxxxxxx";//行情服务器的登陆账户名
std::string quote_password = "xxxxxx";//行情服务器的登陆密码
XTP_PROTOCOL_TYPE protocol_type = XTP_PROTOCOL_TCP;//Level1服务器通常使用TCP,Level2服务器请用UDP,公网测试环境均为TCP,具体以运营通知为准
std::string local_ip = "xxx.xxx.xxx.xxx";//本地网卡对应的ip

int main()
{
	uint8_t client_id = 1;//一个进程一个client id,可在[1, 99]区间内任选,并固定下来
	std::string save_file_path = "./";//保存xtp api日志的路径,需要有可读写权限
	XTP_LOG_LEVEL log_level = XTP_LOG_LEVEL_DEBUG;//xtp api日志的输出级别,建议调试时使用debug级别,正常运行时使用info级别
												  ///创建QuoteApi
	m_pQuoteApi = XTP::API::QuoteApi::CreateQuoteApi(client_id, save_file_path.c_str(), log_level);
	if (NULL == m_pQuoteApi)
	{
		//创建API失败
		return 0;
	}

	///设定行情服务器超时时间,单位为秒,默认是15s,调试时可以设定大点
	uint32_t heat_beat_interval = 15;
	m_pQuoteApi->SetHeartBeatInterval(heat_beat_interval);

	//以下代码段如果是TCP连接的话,需要跳过
	//设定UDP本地缓存buffer大小,单位为MB
	//int buffer_size = 512;//2.2.30.7以上版本api,建议不超过512,最大仅支持1024
	//m_pQuoteApi->SetUDPBufferSize(buffer_size);
	//设定是否输出异步日志
	//bool  log_output_flag = true;//刚实盘运行时,或者调试测试时,建议开启,实盘运行正常后,可以关闭
	//m_pQuoteApi->SetUDPSeqLogOutPutFlag(log_output_flag);

	//创建Spi实例
	MyQuoteSpi* m_pQuoteSpi = new MyQuoteSpi();
	if (NULL == m_pQuoteSpi)
	{
		//创建行情Spi失败
		return 0;
	}

	//注册Spi
	m_pQuoteApi->RegisterSpi(m_pQuoteSpi);

	//登陆行情服务器
	int ret = m_pQuoteApi->Login(quote_server_ip.c_str(), quote_server_port, quote_username.c_str(), quote_password.c_str(), protocol_type, local_ip.c_str());
	if (0 != ret)
	{
		// 登录失败,获取错误信息
		XTPRI* error_info = m_pQuoteApi->GetApiLastError();
		std::cout << "login to server error, " << error_info->error_id << " : " << error_info->error_msg << std::endl;

		return 0;
	}

	// 登录成功
	//TODO: 用户逻辑,例如查询静态数据、订阅行情等,以下以查询沪市静态信息为例

	//查询沪市行情静态信息
	m_pQuoteApi->QueryAllTickersFullInfo(XTP_EXCHANGE_SH);

	//保持主线程,防止程序退出
	while (true)
	{
#ifdef _WIN32
		Sleep(10000);
#else
		sleep(10);
#endif // _WIN32		
	}
	

	return 0;
}

MyQuoteSpi类相关定义和实现文件。 quote_spi.h文件

#include "xtp_quote_api.h"

using namespace XTP::API;

class MyQuoteSpi : public QuoteSpi
{
public:
	MyQuoteSpi();
	~MyQuoteSpi();

	///行情服务器断线通知
	void OnDisconnected(int reason);

	///订阅快照行情应答
	void OnSubMarketData(XTPST *ticker, XTPRI *error_info, bool is_last);

	///订阅快照行情应答
	void OnUnSubMarketData(XTPST *ticker, XTPRI *error_info, bool is_last);

	///查询合约完整静态信息的应答
	void OnQueryAllTickersFullInfo(XTPQFI* ticker_info, XTPRI *error_info, bool is_last);

	///快照行情通知,包含买一卖一队列
	void OnDepthMarketData(XTPMD *market_data, int64_t bid1_qty[], int32_t bid1_count, int32_t max_bid1_count, int64_t ask1_qty[], int32_t ask1_count, int32_t max_ask1_count);

	//TODO:如果需要订阅其他行情,请继续重写其他行情回调函数

private:
	void subscribeMarketData();//订阅行情

};

quote_spi.cpp文件

#include "quote_spi.h"
#include <string>
#include <iostream>
#ifdef _WIN32
#include <windows.h>
#else
#include <unistd.h>
#endif // _WIN32

using namespace std;

extern XTP::API::QuoteApi* m_pQuoteApi;
extern std::string quote_server_ip;//行情服务器ip地址
extern int quote_server_port;//行情服务器端口port
extern std::string quote_username;//行情服务器的登陆账户名
extern std::string quote_password;//行情服务器的登陆密码
extern XTP_PROTOCOL_TYPE protocol_type;//Level1服务器使用TCP,Level2服务器请用UDP,公网测试环境均为TCP,具体以运营通知为准
extern std::string local_ip;//本地网卡对应的ip

MyQuoteSpi::MyQuoteSpi()
{
}

MyQuoteSpi::~MyQuoteSpi()
{
}

void MyQuoteSpi::OnDisconnected(int reason)
{
	//行情服务器断线后,此函数会被调用
	//TODO:重新login,并在login成功后,再次订阅

	cout << "Disconnect from quote server. " << endl;

	//重新登陆行情服务器
	int ret = -1;
	while (0 != ret)
	{
		ret = m_pQuoteApi->Login(quote_server_ip.c_str(), quote_server_port, quote_username.c_str(), quote_password.c_str(), protocol_type, local_ip.c_str());
		if (0 != ret)
		{
			// 登录失败,获取错误信息
			XTPRI* error_info = m_pQuoteApi->GetApiLastError();
			cout << "login to server error, " << error_info->error_id << " : " << error_info->error_msg << endl;

			//等待10s以后再次连接,可修改此等待时间,建议不要小于3s
#ifdef _WIN32
			Sleep(10000);
#else
			sleep(10);
#endif // _WIN32	
		}
	}

	//重连成功
	cout << "login to server success. " << endl;
	//再次订阅行情快照
	subscribeMarketData();

}

void MyQuoteSpi::OnSubMarketData(XTPST *ticker, XTPRI *error_info, bool is_last)
{
	if (error_info && error_info->error_id != 0)
	{
		//订阅失败
		cout << "OnSubMarketData -----" << "error_id = " << error_info->error_id << ", error_msg = " << error_info->error_msg << endl;
		return;
	}

	//订阅成功
	//TODO:当is_last == true时,触发其他用户逻辑	
}

void MyQuoteSpi::OnUnSubMarketData(XTPST *ticker, XTPRI *error_info, bool is_last)
{
	if (error_info && error_info->error_id != 0)
	{
		//取消订阅失败
		cout << "OnUnSubMarketData -----" << "error_id = " << error_info->error_id << ", error_msg = " << error_info->error_msg << endl;
		return;
	}

	//退阅成功
	//TODO:当is_last == true时,触发其他用户逻辑	
}

void MyQuoteSpi::OnQueryAllTickersFullInfo(XTPQFI* ticker_info, XTPRI *error_info, bool is_last)
{
	if (error_info && error_info->error_id != 0)
	{
		//查询失败
		cout << "OnQueryAllTickersFullInfo -----" << "error_id = " << error_info->error_id << ", error_msg = " << error_info->error_msg << endl;
		return;
	}

	//查询成功
	//TODO:将查询结果缓存

	//如果是最后一个响应,通知后续逻辑
	if (is_last == true)
	{
		//TODO:通知后续逻辑,例如可以在查询沪市结束后,再次发起查询深市的查询请求
		if (ticker_info->exchange_id == XTP_EXCHANGE_SH)
		{
			//当沪市的静态行情查询完毕后,查询深市行情静态信息
			m_pQuoteApi->QueryAllTickersFullInfo(XTP_EXCHANGE_SZ);
		}
		else
		{
			//TODO:触发后续逻辑,例如可以订阅快照行情
			subscribeMarketData();
		}
	}
}

void MyQuoteSpi::OnDepthMarketData(XTPMD *market_data, int64_t bid1_qty[], int32_t bid1_count, int32_t max_bid1_count, int64_t ask1_qty[], int32_t ask1_count, int32_t max_ask1_count)
{
	//收到行情快照数据
	//TODO:用户处理逻辑,注意此处不能仅仅保存数据的指针,指针所指向的内存数据将在此函数return后失效
}

void MyQuoteSpi::subscribeMarketData()
{
	// 申请内存,初始化订阅参数
	int ticker_count = 2;//需要订阅行情的证券代码数量,可根据实际订阅需求改动
	char **ppInstrumentID = new char*[ticker_count];
	for (int i = 0; i < ticker_count; i++)
	{
		ppInstrumentID[i] = new char[XTP_TICKER_LEN];
	}
	strcpy_s(ppInstrumentID[0], XTP_TICKER_LEN, "600000");
	strcpy_s(ppInstrumentID[1], XTP_TICKER_LEN, "600001");
	//订阅沪市的600000、600001两只股票快照行情 
	m_pQuoteApi->SubscribeMarketData(ppInstrumentID, ticker_count, XTP_EXCHANGE_SH);

	// 释放内存
	for (int i = 0; i < ticker_count; i++) {
		delete[] ppInstrumentID[i];
		ppInstrumentID[i] = NULL;
	}
	delete[] ppInstrumentID;
	ppInstrumentID = NULL;
}