赞
踩
很多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是以下字段的组合:
对于不涉及套期保值和组合合约交易的大部分投资者来说,可以忽略HedgeFlag和TradeType字段.如果不考虑自成交的情况,可以忽略Direction字段。同时,由于CTP中不同的交易日的成交编号有可能重复(不能扔掉开仓日期),因此对单个账户的持仓明细的key可以简化为:
开仓日期(OpenDate) + 开仓成交编号(TradeID) + 交易所代码(ExchangeID)
开发者可以根据此KEY,来区分及储存持仓明细。
持仓的KEY是以下字段的组合:
目前,上期所(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秒)。用户可以通过一些字段来对查询范围做出限制,如可以不填BrokerID和Investor, 若如此做, 则为默认查询登录的此账户的持仓明细. 查询时, 可以不填InstrumentID合约代码或ExchangeID交易所代码, 若如此做, 则为InstrumentID或ExchangeID不做限制, 即查询满足其他条件的任意合约或任意交易所的合约的持仓明细。
举个例子:
1. InstrumentID填"IF2409",其他填为空,则查询账户中的所有的IF2409合约的持仓明细,在上面的例子中,会查询返回2条记录。
2. ExchangeID填"DCE",其他填为空,则查询账户中的所有的DCE交易所(大商所)的合约的持仓明细,在上面的例子中,会查询返回0条记录。
查询持仓明细的响应OnRspQryInvestorPositionDetail中:
在上面的例子里,如果不限制查询的条件则能查询到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;
- }
- }
查询持仓和查询持仓明细的方法类似,这里不做赘述。
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的使用!
Copyright © 2003-2013 www.wpsshop.cn 版权所有,并保留所有权利。