CTP查询持仓和持仓明细的那些事儿

很多CTP API初学者遇到的一个头疼的事情,就是持仓和持仓明细的查询。这里简单介绍一下这二者的查询的处理,希望对大家有所帮助。

首先讲一下啥是持仓,以及啥是持仓明细

CTP里的持仓明细,则是由开仓成交产生的逐笔持仓数据,而持仓,实际是持仓明细按合约、买卖方向、持仓日期类型等汇总而成的持仓数据,可能把它叫"持仓汇总"会更贴切。

举个例子来方便大家理解:

假设在沪深300指数六月(IF2406)和九月(IF2409)合约上初始时没有持仓。今天(2024年3月28日)陆续在这两个合约上分别报入了一个委托,而且这两个委托都是开仓20手,并且都成交了。订单和成交流水如下:

合约代码 订单编号 方向 报单价格/元 成交编号 成交数量/手 成交价格/元
IF2406 00001 买入/开仓 3500 T10000 20 3500
IF2409 00002 卖出/开仓 3450 T10086 8 3450.2
T10099 12 3450.4

则最终账户里有3笔持仓明细,如下所示:

持仓明细序号 合约代码 持仓明细买卖方向 开仓成交编号 开仓日期 开仓成交价格/元 数量/手
1 IF2406 买入 T10000 20240328 3500 20
2 IF2409 卖出 T10086 20240328 3450.2 8
3 IF2409 卖出 T10099 20240328 3450.4 12

最终有2笔持仓,如下所示:

持仓序号 合约代码 持仓方向 持仓日期类型 持仓均价/元 数量/手
1 IF2406 多头 今仓 3500 20
2 IF2409 空头 今仓 3450.32 20

聪明的你可能已经猜到了,在同一个账户里,持仓持仓明细的KEY是什么。

持仓明细的KEY是以下字段的组合:

  • 买卖方向(Direction)
  • 开仓日期(OpenDate)
  • 开仓成交编号(TradeID)
  • 成交类型(TradeType)

    • 成交类型一般是组合衍生的成交或普通成交,如果不涉及组合套利合约的交易,一般都是普通成交。
  • 投机套保标志(HedgeFlag)

    • 投机套保标志分为投机和套保(套期保值)等,投机持仓和套保持仓是互相独立的,当然,普通投资者订单一般都是投机标志。

 对于不涉及套期保值和组合合约交易的大部分投资者来说,可以忽略HedgeFlagTradeType字段.如果不考虑自成交的情况,可以忽略Direction字段。同时,由于CTP中不同的交易日的成交编号有可能重复(不能扔掉开仓日期),因此对单个账户的持仓明细的key可以简化为:

开仓日期(OpenDate) + 开仓成交编号(TradeID)

开发者可以根据此KEY,来区分及储存持仓明细

持仓的KEY是以下字段的组合:

  • 持仓多空方向(PosiDirection)
  • 持仓日期类型(PositionDate)
  • 投机套保标志(HedgeFlag)

目前,上期所(SHFE)和能源中心(INE)这两个交易所是区分今仓和昨仓(历史仓),因此它们的持仓在持仓日期类型(PositionDate)上有区分(即THOST_FTDC_PSD_Today今仓和THOST_FTDC_PSD_History昨仓)。其他交易所则不区分,持仓日期类型的值无多大意义,一般值都是THOST_FTDC_PSD_Today(今仓)。

在上面的例子里,由于是中金所合约的持仓,所以查询得到的这两条持仓记录中的持仓日期类型都是THOST_FTDC_PSD_Today(今仓),即使是(当天结算后)到了第二天的交易日,这两个持仓(已经变成了昨仓)它们的持仓日期类型仍然还是THOST_FTDC_PSD_Today(今仓)。

如果你动手能力够强,可以自行由持仓明细合成出持仓汇总

查询持仓明细

CTP查询持仓明细的请求函数是:

int ReqQryInvestorPositionDetail
(CThostFtdcQryInvestorPositionDetailField*pQryInvestorPositionDetail, int nRequestID);

返回值:
0,代表成功。
-1,表示网络连接失败;
-2,表示未处理请求超过许可数;
-3,表示每秒发送请求数超过许可数。

查询持仓明细的响应函数是:

void OnRspQryInvestorPositionDetail
(CThostFtdcInvestorPositionDetailField* pInvestorPositionDetail,
CThostFtdcRspInfoField *pRspInfo, int nRequestID, bool bIsLast);

用户可以根据交易所代码和合约代码等来查询持仓明细。下面是一个示例:

void CCTPTrade::QueryPositionDetail(const std::string& contract)
{
	CThostFtdcQryInvestorPositionDetailField req = { 0 };
	::strcpy(req.BrokerID, m_BrokerID.c_str());
	::strcpy(req.InvestorID, m_userID.c_str());
	::strcpy(req.InstrumentID, contract.c_str());
	::strcpy(req.ExchangeID, "");

	int ret = m_pAPI->ReqQryInvestorPositionDetail(&req, ++m_requestId);
	if (ret != 0) {
		std::cerr << "Query position detail failed!" << std::endl;
		return;
	}
}

         和其他查询类似, 查询持仓明细受到查询流控的影响, 在途查询仅能有一笔, 同时有两次查询时间间隔的限制(一般为间隔1秒)。用户可以通过一些字段来对查询范围做出限制,如可以不填BrokerIDInvestor, 若如此做, 则为默认查询登录的此账户的持仓明细. 查询时, 可以不填InstrumentID合约代码ExchangeID交易所代码, 若如此做, 则为InstrumentIDExchangeID不做限制, 即查询满足其他条件的任意合约或任意交易所的合约的持仓明细

举个例子:

1. InstrumentID填"IF2409",其他填为空,则查询账户中的所有的IF2409合约的持仓明细,在上面的例子中,会查询返回2条记录。

2. ExchangeID填"DCE",其他填为空,则查询账户中的所有的DCE交易所(大商所)的合约的持仓明细,在上面的例子中,会查询返回0条记录。

        查询持仓明细的响应OnRspQryInvestorPositionDetail中:

  1. 若有多条满足条件的持仓明细,则分成多次返回,最后一条的记录bIsLast值为true。
  2. 若没有任何满足条件的持仓明细,则仅返回一次(空的记录),其bIsLast值为true,pInvestorPositionDetail参数空指针

在上面的例子里,如果不限制查询的条件则能查询到3条持仓明细记录,返回第3条记录时,bIslast值为true。

需要提到的是,有时候会查询得到持仓数量为0的持仓明细记录,这表明这个持仓明细在今天已经被完全平仓了。盘后结算时,已全部平仓的持仓明细将被清除,第二天就无法再查询到了。

查询持仓

CTP查询持仓的请求函数是:

int ReqQryInvestorPosition
(CThostFtdcQryInvestorPositionField *pQryInvestorPosition, int nRequestID);

返回值:
0,代表成功。
-1,表示网络连接失败;
-2,表示未处理请求超过许可数;
-3,表示每秒发送请求数超过许可数。

查询持仓的响应函数是:

void OnRspQryInvestorPosition
(CThostFtdcInvestorPositionField *pInvestorPosition,
CThostFtdcRspInfoField *pRspInfo, int nRequestID, bool bIsLast);

用户可以根据交易所代码和合约代码等来查询持仓。下面是一个示例:

void CCTPTrade::QueryPosition(const std::string& contract)
{
	CThostFtdcQryInvestorPositionField req = { 0 };
	::strcpy(req.BrokerID, m_BrokerID.c_str());
	::strcpy(req.InvestorID, m_userID.c_str());
	::strcpy(req.InstrumentID, contract.c_str());
	::strcpy(req.ExchangeID, "");

	int ret = m_pAPI->ReqQryInvestorPosition(&req, ++m_requestId);
	if (ret != 0) {
		std::cerr << "Query position failed!" << std::endl;
		return;
	}
}

查询持仓和查询持仓明细的方法类似,这里不做赘述。

CThostFtdcInvestorPositionField 持仓数据结构部分字段的说明:

Position: 表示当前持仓数量(手数)

TodayPosition: 表示今日新开仓数量,也就是当前持仓数量里的今仓的数量

YdPosition: 表示昨日收盘时持仓数量(≠ 当前的昨仓数量, 静态, 日间不随着开平仓而变化)

YdStrikeFrozen: 该字段是给个股期权用的, 期货期权里一直保持为0

OpenVolume: 当日累计开仓量,一天之内只会增加不会减少

CloseVolume: 当日累计平仓量,一天之内只会增加不会减少

OpenAmount: 当日开仓成交的成交额,一天之内只会增加不会减少

CloseAmount: 当日平仓成交的成交额,一天之内只会增加不会减少

LongFrozen: 多头冻结. 未成交的买入委托(含开仓和平仓)的未成交数量

ShortFrozen: 空头冻结. 未成交的卖出委托(含开仓和平仓)的未成交数量

OpenCost: 开仓成本, 等于汇总的各笔持仓明细的开仓成本的和

PositionCost: 持仓成本, 等于汇总的各笔持仓明细的持仓成本的和

FrozenMargin: 冻结的保证金, 开仓未成交的委托占用的冻结的保证金

PositionProfit: (当日的)(逐日盯市)持仓盈亏. 期权没有持仓盈亏, 为0.

CloseProfit: (当日的)(逐日盯市)平仓盈亏. 期权没有平仓盈亏, 为0.

        例如, CF101的多头持仓4手, 平仓掉1手, 则这笔平仓成交产生的平仓盈亏会计入到剩下的这3手的CF101多头持仓记录中。

持仓字段中没有当前昨仓数量, 开仓均价, 持仓均价, 可用持仓(即可平持仓)数量等字段, 它们需要我们自己来算:

当前的昨仓数量 = Position - TodayPosition

开仓均价 = OpenCost / (Position * 合约乘数)

持仓均价 = PositionCost / (Position * 合约乘数)

可用数量:

        可用数量(对于多头持仓) = Position - ShortFrozen - CombShortFrozen

        可用数量(对于空头持仓) = Position - LongFrozen - CombLongFrozen

持仓明细类似,有时候会查询得到持仓数量为0的持仓记录,这表明这个持仓在今天已经被完全平仓了,或者今天曾有过开仓报单但报单还没有成交过。当然,这种持仓中的平仓盈亏(CloseProfit)和平仓量(CloseVolume)等数据可能是有数值(而非0)的。盘后结算时,已全部平仓的持仓将被清除,第二天就无法再查询到了。

如果有更多想要了解的,欢迎加入QQ交流群 736174420,一起讨论交流CTP API的使用!

本图文内容来源于网友网络收集整理提供,作为学习参考使用,版权属于原作者。
THE END
分享
二维码
< <上一篇
下一篇>>