Skip to content

新股申购逻辑示例代码

目录


1. 总体步骤说明

1.1. 继承Spi

代码通过#include "xtpx_trader_api.h",将xtpxtraderapi.lib中声明的类和数据类型包括进来,该头文件中有两个重要的基类:

TraderApi类提供交易相关的请求接口,本示例中调用查询新股申购信息列表、查询新股额度信息和报单接口。

TraderSpi类提供交易相关的回调接口,本示例需要继承该类并重写查询新股申购信息列表和额度信息的回调接口。

1.2. 初始化Api

在方法(initialize)里完成MyTraderApi的初始化,创建TraderSpi实例,并向TraderApi实例注册;

1.3. 登录交易服务器

登录交易服务器,登录前设置必要参数,包括设置软件开发版本号,软件开发Key,心跳检测时间间隔等,登录接口返回会话ID表示登录成功;

1.4. 调用查询新股申购额度信息接口QueryIPOQuotaInfo

调用api的QueryIPOQuotaInfo方法发出查询新股申购额度信息的请求。 示例代码如下:

cpp
// 查询用户新股申购额度信息
void MyTraderApi::queryIPOQuotaInfo()
{
	int request_id = 1;//用户自定义,用来标识查询
	if (user_trade_api_) {
		int ret = user_trade_api_->QueryIPOQuotaInfo(session_id_, request_id);
		if (ret != 0) {
			// 查询新股申购额度发送失败,获取发送失败的错误码
			XTPRI *error_info = user_trade_api_->GetApiLastError();
			std::cout << "query ipo quota error, " << error_info->error_id << " : " << error_info->error_msg << std::endl;
		}
	}
}

1.5. 回调函数OnQueryIPOQuotaInfo中缓存新股申购额度信息

在spi的回调函数OnQueryIPOQuotaInfo中缓存新股申购额度信息并缓存备用。 示例代码如下:

cpp

int32_t quota_sh; //沪市新股申购额度
int32_t quota_tech;//科创板新股申购额度
int32_t quota_sz;//深市新股申购额度

void MyTraderSpi::OnQueryIPOQuotaInfo(XTPQueryIPOQuotaRsp *quota_info, XTPRI *error_info, int request_id, bool is_last, uint64_t session_id)
{
	if ((error_info) && (error_info->error_id != 0)) {
		//查询有错误,输出错误信息
		std::cout << "OnQueryIPOQuotaInfo error, " << error_info->error_id << " : " << error_info->error_msg << std::endl;
		return;
	}

	//存储各个市场的新股申购额度
	calcIPOquota(quota_info);

	if (is_last) {

		// 示例代码只是spi实例通知parent去获取新股信息,方便api实例继续运行
		_f_get_ipo_info();
	}
}

//存储各个市场的新股申购额度
void MyTraderSpi::calcIPOquota(XTPQueryIPOQuotaRsp * data)
{
	if (!data) return;

	switch (data->market)
	{
	case XTP_MKT_SZ_A:
	{
		quota_sz = data->quantity;
	}
	break;
	case XTP_MKT_SH_A:
	{
		quota_tech = data->tech_quantity;
		quota_sh = data->quantity;
	}
	break;
	default:
		break;
	}
}

1.6. 调用查询当日新股信息接口QueryIPOInfoList

调用api的QueryIPOInfoList方法发出查询新股申购信息列表请求。 示例代码如下:

cpp
// 查询今日新股申购信息列表
void MyTraderApi::queryIPOInfoList()
{
	int request_id = 1;//用户自定义,用来标识查询
	if (user_trade_api_) {
		int ret = user_trade_api_->QueryIPOInfoList(session_id_, request_id);
		if (ret != 0) {
			// 下单发送失败,获取下单发送失败的错误码
			XTPRI *error_info = user_trade_api_->GetApiLastError();
			std::cout << "query ipo info error, " << error_info->error_id << " : " << error_info->error_msg << std::endl;
		}
	}
}

1.7. 回调函数OnQueryIPOInfoList中缓存新股信息

在spi的回调函数OnQueryIPOInfoList中获取到新股申购信息列表并缓存备用。 示例代码如下:

cpp

void MyTraderSpi::OnQueryIPOInfoList(XTPQueryIPOTickerRsp *ipo_info, XTPRI *error_info, int request_id, bool is_last, uint64_t session_id)
{
	//查询有错误,输出错误信息
	if ((error_info) && (error_info->error_id != 0)) {
		std::cout << "OnQueryIPOInfoList error, " << error_info->error_id << " : " << error_info->error_msg << std::endl;
		return;
	}

	//计算每只新股的可申购额度
	calIPOInfoAvaQty(ipo_info);

	if (is_last) {
		/// TODO:缓存数据发送至UI线程

		// 数据使用完后清空容器
		ipo_info_buffer_.clear();
	}
}

1.8. 计算出可申购的新股信息

能够申购的最大数量 = min(可申购额度,最大允许申购数量),并且 申购委托数量必须是申购单元的整数倍。 根据可申购新股信息、新股申购额度信息、申购单元,计算出此新股的可申购数量。将计算后的数据缓存至ipo_info_buffer_备用。

示例代码如下:

cpp
// 根据申购额度,申购单元和最大申购量计算可申购量
//计算每只新股的最后可申购额度
void MyTraderSpi::calIPOInfoAvaQty(XTPQueryIPOTickerRsp * data)
{
	if (!data) return;

	int32_t qty_ava = 0;

	switch (data->market)
	{
	case XTP_MKT_SH_A:
	{
		if (data->ticker_type == XTP_TICKER_TYPE_TECH_STOCK)
		{
			qty_ava = min(data->qty_upper_limit, quota_tech);
		}
		else
		{
			qty_ava = min(data->qty_upper_limit, quota_sh);
		}
	}
	break;
	case XTP_MKT_SZ_A:
	{
		qty_ava = min(data->qty_upper_limit, quota_sz);
	}
	break;
	default:
		break;
	}

	//按照最小申购单元处理申购额度
	int num = qty_ava % data->unit;
	qty_ava -= num;

	if (qty_ava == 0) return; //如果申购数量为0,就跳过,无需申购

	MyIpoInfo ipo_info;
	memset(&ipo_info, 0, sizeof(MyIpoInfo));
	memcpy(&ipo_info.ipo_info, data, sizeof(XTPQueryIPOTickerRsp));
	ipo_info.qty_available = qty_ava;
	
	//缓存新股申购信息
	ipo_info_buffer_.push_back(ipo_info);
}

1.9. 报单

在spi收到最后一条查询结果时,通知api报单。 遍历缓存数据,根据其可申购数量,做新股申购。 示例代码如下:

cpp
// 报送新股申购订单
void MyTraderApi::insertOrder()
{
	for (std::vector<MyIpoInfo>::iterator it = m_trader_spi->getIPOInfoData()->begin(); it != m_trader_spi->getIPOInfoData()->end(); it++)
	{

		// XTPOrderInsertInfo报单结构体初始化
		XTPOrderInsertInfo order_info;
		memset(&order_info, 0, sizeof(XTPOrderInsertInfo));
		// 参数赋值
		order_info.market = it->ipo_info.market;
		strcpy_s(order_info.ticker, XTP_TICKER_LEN, it->ipo_info.ticker);   // XTP_TICKER_LEN:存放证券代码的字符串长度
		order_info.business_type = XTP_BUSINESS_TYPE_IPOS;          // XTP_BUSINESS_TYPE_IPOS为新股申购的业务类型
		order_info.side = XTP_SIDE_BUY;            // 买
		order_info.position_effect = XTP_POSITION_EFFECT_INIT;      // position_effect开平标志,除期权以外,都使用该值
		order_info.price_type = XTP_PRICE_LIMIT;   // 价格类型为限价
		order_info.quantity = it->qty_available;                   // 申购单位,深证500股,上证1000股,科创板500,委托数量是这些单位的整数倍
		if (user_trade_api_ && session_id_ != 0)
		{
			// InsertOrder下单接口
			uint64_t xtp_id = user_trade_api_->InsertOrder(&order_info, session_id_);
			if (0 == xtp_id)//=0说明下单发送失败
			{
				// 下单发送失败,获取下单发送失败的错误码
				XTPRI *error_info = user_trade_api_->GetApiLastError();
				std::cout << "insert ipo orders error, " << error_info->error_id << " : " << error_info->error_msg << std::endl;
			}
		}
	}
}

2. 完整示例代码

首先初始化MyTraderApi类,后连接服务端设置参数,执行登录,查询新股信息,报单等操作。

完整示例代码如下:


以下是MyTraderSpi.h文件

cpp
#include "xtpx_trader_api.h"

#include <iostream>
#include <vector>
#include <functional>

using namespace std;
using namespace XTPX::API;

//每只新股的可申购信息结构体
typedef struct MyIPOInfo {
	XTPQueryIPOTickerRsp ipo_info;
	int32_t qty_available;
} MyIpoInfo;

class MyTraderSpi : public TraderSpi
{
public:
	explicit MyTraderSpi();
	~MyTraderSpi();

	// 请求查询今日新股申购信息列表的响应
	void OnQueryIPOInfoList(XTPQueryIPOTickerRsp *ipo_info, XTPRI *error_info, int request_id, bool is_last, uint64_t session_id);
	// 请求查询用户新股申购额度信息的响应
	void OnQueryIPOQuotaInfo(XTPQueryIPOQuotaRsp *quota_info, XTPRI *error_info, int request_id, bool is_last, uint64_t session_id);

	// 查询请求完成后,通知获取新股信息
	void bindTraderGetIPOInfoFunc(std::function<void(void)> f) { _f_get_ipo_info = f; }
	// 查询请求完成通知报单
	void bindTraderInsertOrderFunc(std::function<void(void)> f) { _f_insert_order = f; }

	// 数据
	std::vector<MyIpoInfo> *getIPOInfoData() { return &ipo_info_buffer_; }

protected:
	// 计算市场的申购额度
	void calcIPOquota(XTPQueryIPOQuotaRsp* data);
	// 计算每只新股的可申购数量
	void calIPOInfoAvaQty(XTPQueryIPOTickerRsp* data);

private:
	std::vector<MyIpoInfo> ipo_info_buffer_;

	std::function<void(void)> _f_get_ipo_info;
	std::function<void(void)> _f_insert_order;

	int32_t quota_sh; //沪市新股申购额度
	int32_t quota_tech;//科创板新股申购额度
	int32_t quota_sz;//深市新股申购额度
};

以下是MyTraderSpi.cpp文件

cpp
#include "MyTraderSpi.h"
#include <algorithm>

MyTraderSpi::MyTraderSpi() {
	quota_sh = 0;
	quota_tech = 0;
	quota_sz = 0;
}
MyTraderSpi::~MyTraderSpi() { }

void MyTraderSpi::OnQueryIPOInfoList(XTPQueryIPOTickerRsp *ipo_info, XTPRI *error_info, int request_id, bool is_last, uint64_t session_id)
{
	//查询有错误,输出错误信息
	if ((error_info) && (error_info->error_id != 0)) {
		std::cout << "OnQueryIPOInfoList error, " << error_info->error_id << " : " << error_info->error_msg << std::endl;
		return;
	}

	//计算每只新股的可申购额度
	calIPOInfoAvaQty(ipo_info);

	if (is_last) {
		/// TODO:缓存数据发送至UI线程

		// 示例代码只是spi实例通知parent去报新股申购订单,方便api实例继续运行
		_f_insert_order();
		// 报单后清空容器
		ipo_info_buffer_.clear();
	}
}

void MyTraderSpi::OnQueryIPOQuotaInfo(XTPQueryIPOQuotaRsp *quota_info, XTPRI *error_info, int request_id, bool is_last, uint64_t session_id)
{
	if ((error_info) && (error_info->error_id != 0)) {
		//查询有错误,输出错误信息
		std::cout << "OnQueryIPOQuotaInfo error, " << error_info->error_id << " : " << error_info->error_msg << std::endl;
		return;
	}

	//存储各个市场的新股申购额度
	calcIPOquota(quota_info);

	if (is_last) {

		// 示例代码只是spi实例通知parent去获取新股信息,方便api实例继续运行
		_f_get_ipo_info();
	}
}

void MyTraderSpi::calcIPOquota(XTPQueryIPOQuotaRsp * data)
{
	if (!data) return;

	switch (data->market)
	{
	case XTP_MKT_SZ_A:
	{
		quota_sz = data->quantity;
	}
	break;
	case XTP_MKT_SH_A:
	{
		quota_tech = data->tech_quantity;
		quota_sh = data->quantity;
	}
	break;
	default:
		break;
	}
}

//计算每只新股的最后可申购额度
void MyTraderSpi::calIPOInfoAvaQty(XTPQueryIPOTickerRsp * data)
{
	if (!data) return;

	int32_t qty_ava = 0;

	switch (data->market)
	{
	case XTP_MKT_SH_A:
	{
		if (data->ticker_type == XTP_TICKER_TYPE_TECH_STOCK)
		{
			qty_ava = min(data->qty_upper_limit, quota_tech);
		}
		else
		{
			qty_ava = min(data->qty_upper_limit, quota_sh);
		}
	}
	break;
	case XTP_MKT_SZ_A:
	{
		qty_ava = min(data->qty_upper_limit, quota_sz);
	}
	break;
	default:
		break;
	}

	//按照最小申购单元处理申购额度
	int num = qty_ava % data->unit;
	qty_ava -= num;

	if (qty_ava == 0) return; //如果申购数量为0,就跳过,无需申购

	MyIpoInfo ipo_info;
	memset(&ipo_info, 0, sizeof(MyIpoInfo));
	memcpy(&ipo_info.ipo_info, data, sizeof(XTPQueryIPOTickerRsp));
	ipo_info.qty_available = qty_ava;
	
	//缓存新股申购信息
	ipo_info_buffer_.push_back(ipo_info);
}

以下是MyTraderApi.h文件

cpp
#include "xtpx_trader_api.h"
#include "MyTraderSpi.h"

#include <iostream>
#include <vector>

using namespace std;
using namespace XTPX::API;

class MyTraderApi
{
public:
	explicit MyTraderApi();
	~MyTraderApi();

	// 初始化
	bool initialize();
	// 释放
	void release();
	// 登录
	uint64_t login();

	// 请求查询今日新股申购信息列表
	void queryIPOInfoList();
	// 请求查询用户新股申购额度信息
	void queryIPOQuotaInfo();

	// 报单
	void insertOrder();

private:
	TraderApi *user_trade_api_;
	MyTraderSpi *m_trader_spi;

	uint64_t session_id_;

};

以下是MyTraderApi.cpp文件

cpp
#include "MyTraderApi.h"

#include <algorithm>

MyTraderApi::MyTraderApi()
{
	user_trade_api_ = nullptr;
	m_trader_spi = nullptr;

	session_id_ = 0;
}

MyTraderApi::~MyTraderApi()
{
	if (user_trade_api_ != NULL) {
		user_trade_api_->Logout(session_id_);
		user_trade_api_->Release();
	}
}

bool MyTraderApi::initialize()
{
	// 创建并初始化交易API
	user_trade_api_ = XTPX::API::TraderApi::CreateTraderApi(1, "./", XTP_LOG_LEVEL_DEBUG);
	if (user_trade_api_)
	{
		// 注册回调接口
		m_trader_spi = new MyTraderSpi();
		user_trade_api_->RegisterSpi(m_trader_spi);
		m_trader_spi->bindTraderGetIPOInfoFunc(std::bind(&MyTraderApi::queryIPOInfoList, this));
		m_trader_spi->bindTraderInsertOrderFunc(std::bind(&MyTraderApi::insertOrder, this));
		// 登录前参数设置
		user_trade_api_->SetHeartBeatInterval(15);
		user_trade_api_->SetSoftwareKey("xxxxxxxxxx");
		user_trade_api_->SetSoftwareVersion("xx.xx.xx.xx");
		// 设置公有流(订单响应、成交回报)重传方式
		user_trade_api_->SubscribePublicTopic(XTP_TERT_QUICK);

		return true;
	}
	return false;
}

// 释放
void MyTraderApi::release()
{
	if (user_trade_api_ != NULL) {
		user_trade_api_->Logout(session_id_);
	}
}

// 登录
uint64_t MyTraderApi::login()
{
	// 用户请根据实际情况修改登录参数
	std::string trade_server_ip = "xxx.xxx.xxx.xxx";
	int trade_server_port = xx;
	std::string account_name = "xxxxxxxx";
	std::string account_pw = "xxxxxx";

	uint64_t ret = user_trade_api_->Login(trade_server_ip.c_str(), trade_server_port, account_name.c_str(), account_pw.c_str(), XTP_PROTOCOL_TCP);
	if (ret != 0) // 登录成功
	{
		session_id_ = ret;
	}
	else // 登录失败
	{
		XTPRI* error_info = user_trade_api_->GetApiLastError();
		std::cout << "login to server error, " << error_info->error_id << " : " << error_info->error_msg << std::endl;
	}
	return ret;
}

// 查询今日新股申购信息列表
void MyTraderApi::queryIPOInfoList()
{
	int request_id = 1;//用户自定义,用来标识查询
	if (user_trade_api_) {
		int ret = user_trade_api_->QueryIPOInfoList(session_id_, request_id);
		if (ret != 0) {
			// 下单发送失败,获取下单发送失败的错误码
			XTPRI *error_info = user_trade_api_->GetApiLastError();
			std::cout << "query ipo info error, " << error_info->error_id << " : " << error_info->error_msg << std::endl;
		}
	}
}

// 查询用户新股申购额度信息
void MyTraderApi::queryIPOQuotaInfo()
{
	int request_id = 1;//用户自定义,用来标识查询
	if (user_trade_api_) {
		int ret = user_trade_api_->QueryIPOQuotaInfo(session_id_, request_id);
		if (ret != 0) {
			// 查询新股申购额度发送失败,获取发送失败的错误码
			XTPRI *error_info = user_trade_api_->GetApiLastError();
			std::cout << "query ipo quota error, " << error_info->error_id << " : " << error_info->error_msg << std::endl;
		}
	}
}

// 报送新股申购订单
void MyTraderApi::insertOrder()
{
	for (std::vector<MyIpoInfo>::iterator it = m_trader_spi->getIPOInfoData()->begin(); it != m_trader_spi->getIPOInfoData()->end(); it++)
	{

		// XTPOrderInsertInfo报单结构体初始化
		XTPOrderInsertInfo order_info;
		memset(&order_info, 0, sizeof(XTPOrderInsertInfo));
		// 参数赋值
		order_info.market = it->ipo_info.market;
		strcpy_s(order_info.ticker, XTP_TICKER_LEN, it->ipo_info.ticker);   // XTP_TICKER_LEN:存放证券代码的字符串长度
		order_info.business_type = XTP_BUSINESS_TYPE_IPOS;          // XTP_BUSINESS_TYPE_IPOS为新股申购的业务类型
		order_info.side = XTP_SIDE_BUY;            // 买
		order_info.position_effect = XTP_POSITION_EFFECT_INIT;      // position_effect开平标志,除期权以外,都使用该值
		order_info.price_type = XTP_PRICE_LIMIT;   // 价格类型为限价
		order_info.quantity = it->qty_available;                   // 申购单位,深证500股,上证1000股,科创板500,委托数量是这些单位的整数倍
		if (user_trade_api_ && session_id_ != 0)
		{
			// InsertOrder下单接口
			uint64_t xtp_id = user_trade_api_->InsertOrder(&order_info, session_id_);
			if (0 == xtp_id)//=0说明下单发送失败
			{
				// 下单发送失败,获取下单发送失败的错误码
				XTPRI *error_info = user_trade_api_->GetApiLastError();
				std::cout << "insert ipo orders error, " << error_info->error_id << " : " << error_info->error_msg << std::endl;
			}
		}
	}
}

以下是main.cpp文件

cpp
#include <iostream>
#include "MyTraderApi.h"
#ifdef _WIN32
#include <windows.h>
#else
#include <unistd.h>
#endif

using namespace std;

int main(int argc, char *argv[])
{
	MyTraderApi *pTraderApi = new MyTraderApi;

	if (pTraderApi)
	{
		bool b_ret = pTraderApi->initialize();
		if (!b_ret)
		{
			// 初始化失败程序退出
			return -1;
		}
		uint64_t ret = pTraderApi->login();
		if (ret != 0)
		{
			// 先查询新股额度,后续查询和报单将在回调函数中触发
			pTraderApi->queryIPOQuotaInfo();

		}
		//主线程循环,防止进程退出
		while (true)
		{
#ifdef _WIN32
			Sleep(1000);
#else
			sleep(1);
#endif
		}
	}

	return 0;
}

鲁ICP备09057662号 | 鲁公网安备 37010302000722号