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

作者 : admin 本文共5306个字,预计阅读时间需要14分钟 发布时间: 2024-06-9 共2人阅读

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

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

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

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

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

合约代码订单编号方向报单价格/元成交编号成交数量/手成交价格/元
IF240600001买入/开仓3500T10000203500
IF240900002卖出/开仓3450T1008683450.2
T10099123450.4

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

持仓明细序号合约代码持仓明细买卖方向开仓成交编号开仓日期开仓成交价格/元数量/手
1IF2406买入T1000020240328350020
2IF2409卖出T10086202403283450.28
3IF2409卖出T10099202403283450.412

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

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

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

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

  • 买卖方向(Direction)
  • 开仓日期(OpenDate)
  • 开仓成交编号(TradeID)
  • 交易所代码(ExchangeID)
  • 成交类型(TradeType)
    • 成交类型一般是组合衍生的成交或普通成交,如果不涉及组合套利合约的交易,一般都是普通成交。
  • 投机套保标志(HedgeFlag)
    • 投机套保标志分为投机和套保(套期保值)等,投机持仓和套保持仓是互相独立的,当然,普通投资者订单一般都是投机标志。

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

开仓日期(OpenDate) + 开仓成交编号(TradeID) + 交易所代码(ExchangeID)

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

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

  • 合约代码(InstrumentID)
  • 持仓多空方向(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.

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

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

当前的昨仓数量 = Position TodayPosition

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

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

可用数量:

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

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

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

在上面的例子中,查询IF2409合约的持仓,会得到1条记录,数据如下:

InstrumentID: “IF2409”

PosiDirection: THOST_FTDC_PD_Short (空头方向持仓)

PositionDate: THOST_FTDC_PSD_Today (今日持仓)

HedgeFlag: THOST_FTDC_HF_Speculation (投机)

Position: 20

TodayPosition: 20

YdPosition: 0

YdStrikeFrozen: 0

OpenVolume: 20

CloseVolume: 0

OpenAmount: 20*300*3450.32=20701920

CloseAmount: 0

LongFrozen: 0

ShortFrozen: 0

OpenCost: 20701920

PositionCost: 20701920

FrozenMargin: 0

PositionProfit: (取决于查询时的最新行情价格)

CloseProfit: 0

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

本站无任何商业行为
个人在线分享 » CTP查询持仓和持仓明细的那些事儿
E-->