根据CTP接口计算现手、增仓、开平、对手盘 (1)

0. 欢迎交流

CTP_API技术交流群:767101469

github: https://github.com/nicai0609/JAVA-CTPAPI

 

1、前言

在同花顺软件中有显示全市场的成交明细如下图所示,API中只推自己的成交回报,那全市场的哪来的呢?其实这些数据都是可以根据CTP API接口自己通过计算得到,下面就来讲讲。

 

 

首先来看下CTP的mduserapi中深度行情结构体如下所示

///深度行情
struct CThostFtdcDepthMarketDataField
{
	///交易日
	TThostFtdcDateType	TradingDay;
	///合约代码
	TThostFtdcInstrumentIDType	InstrumentID;
	///交易所代码
	TThostFtdcExchangeIDType	ExchangeID;
	///合约在交易所的代码
	TThostFtdcExchangeInstIDType	ExchangeInstID;
	///最新价
	TThostFtdcPriceType	LastPrice;
	///上次结算价
	TThostFtdcPriceType	PreSettlementPrice;
	///昨收盘
	TThostFtdcPriceType	PreClosePrice;
	///昨持仓量
	TThostFtdcLargeVolumeType	PreOpenInterest;
	///今开盘
	TThostFtdcPriceType	OpenPrice;
	///最高价
	TThostFtdcPriceType	HighestPrice;
	///最低价
	TThostFtdcPriceType	LowestPrice;
	///数量
	TThostFtdcVolumeType	Volume;
	///成交金额
	TThostFtdcMoneyType	Turnover;
	///持仓量
	TThostFtdcLargeVolumeType	OpenInterest;
	///今收盘
	TThostFtdcPriceType	ClosePrice;
	///本次结算价
	TThostFtdcPriceType	SettlementPrice;
	///涨停板价
	TThostFtdcPriceType	UpperLimitPrice;
	///跌停板价
	TThostFtdcPriceType	LowerLimitPrice;
	///昨虚实度
	TThostFtdcRatioType	PreDelta;
	///今虚实度
	TThostFtdcRatioType	CurrDelta;
	///最后修改时间
	TThostFtdcTimeType	UpdateTime;
	///最后修改毫秒
	TThostFtdcMillisecType	UpdateMillisec;
	///申买价一
	TThostFtdcPriceType	BidPrice1;
	///申买量一
	TThostFtdcVolumeType	BidVolume1;
	///申卖价一
	TThostFtdcPriceType	AskPrice1;
	///申卖量一
	TThostFtdcVolumeType	AskVolume1;
	///申买价二
	TThostFtdcPriceType	BidPrice2;
	///申买量二
	TThostFtdcVolumeType	BidVolume2;
	///申卖价二
	TThostFtdcPriceType	AskPrice2;
	///申卖量二
	TThostFtdcVolumeType	AskVolume2;
	///申买价三
	TThostFtdcPriceType	BidPrice3;
	///申买量三
	TThostFtdcVolumeType	BidVolume3;
	///申卖价三
	TThostFtdcPriceType	AskPrice3;
	///申卖量三
	TThostFtdcVolumeType	AskVolume3;
	///申买价四
	TThostFtdcPriceType	BidPrice4;
	///申买量四
	TThostFtdcVolumeType	BidVolume4;
	///申卖价四
	TThostFtdcPriceType	AskPrice4;
	///申卖量四
	TThostFtdcVolumeType	AskVolume4;
	///申买价五
	TThostFtdcPriceType	BidPrice5;
	///申买量五
	TThostFtdcVolumeType	BidVolume5;
	///申卖价五
	TThostFtdcPriceType	AskPrice5;
	///申卖量五
	TThostFtdcVolumeType	AskVolume5;
	///当日均价
	TThostFtdcPriceType	AveragePrice;
	///业务日期
	TThostFtdcDateType	ActionDay;
};

客户调用CTP的行情接口就会收到CTP发来的行情,这个是行情快照。

2、现手和增仓

根据前后两次行情就可以计算出现手,增仓,公式如下。

现手 volume_delta = 后一笔Volume - 前一笔volume (1)

增仓 open_interest_delta = 后一笔OpenInterest - 前一笔OpenInterest (2)

代码如下:

volume_delta = data['Volume'] - self.PreDepthMarketData['Volume']
open_interest_delta = int(data['OpenInterest'] - self.PreDepthMarketData['OpenInterest'])

 

3、开平

根据现手和增仓我们可以计算出开平(不带方向),如下逻辑(3)

增仓=0,现手=0:未知

增仓=0,现手>0:换手

增仓>0,现手=增仓:双开

增仓>0,现手<>增仓:开仓

增仓<0,现手+增仓=0:双平

增仓<0,现手+增仓<>0:平仓

函数如下:

def get_open_interest_delta_forward(open_interest_delta, volume_delta):
        """根据成交量的差和持仓量的差来获取仓位变化的方向
            return: open_interest_delta_forward_enum
        """
        if open_interest_delta == 0 and volume_delta == 0:
            local_open_interest_delta_forward = open_interest_delta_forward_enum.NONE
        elif open_interest_delta == 0 and volume_delta > 0:
            local_open_interest_delta_forward = open_interest_delta_forward_enum.EXCHANGE
        elif open_interest_delta > 0:
            if open_interest_delta - volume_delta == 0:
                local_open_interest_delta_forward = open_interest_delta_forward_enum.OPENFWDOUBLE
            else:
                local_open_interest_delta_forward = open_interest_delta_forward_enum.OPEN
        elif open_interest_delta < 0:
            if open_interest_delta + volume_delta == 0:
                local_open_interest_delta_forward = open_interest_delta_forward_enum.CLOSEFWDOUBLE
            else:
                local_open_interest_delta_forward = open_interest_delta_forward_enum.CLOSE
        return local_open_interest_delta_forward

有了逻辑(3)之后,我们还需要知道价格趋势才能计算出方向,然后才能计算出真正的开平。根据价格比较我们可以计算出当前成交时价格是向上还是向下,如下逻辑(4)

后一笔最新价LastPrice >= 前一笔卖价一AskPrice1 :向上

后一笔最新价LastPrice <= 前一笔买价一BidPrice1 :向下

后一笔最新价LastPrice >= 后一笔卖价一AskPrice1 :向上

后一笔最新价LastPrice <= 后一笔买价一BidPrice1 :向下

除上面四种外:不变

代码如下:

def get_order_forward(last_price, ask_price1, bid_price1, pre_last_price, pre_ask_price1, pre_bid_price1):
        """获取成交的区域,根据当前tick的成交价和上个tick的ask和bid价格进行比对
           return: order_forward_enum
        """
        if TickAnalysis.float_bigger_equal(last_price, pre_ask_price1):
            local_order_forward = order_forward_enum.UP
        elif TickAnalysis.float_smaller_equal(last_price, pre_bid_price1):
            local_order_forward = order_forward_enum.DOWN
        else:
            if TickAnalysis.float_bigger_equal(last_price, ask_price1):
                local_order_forward = order_forward_enum.UP
            elif TickAnalysis.float_smaller_equal(last_price, bid_price1):
                local_order_forward = order_forward_enum.DOWN
            else:
                local_order_forward = order_forward_enum.MIDDLE

        return local_order_forward

 

现在有了逻辑(3)和(4)之后,可以计算出真正的开平了,逻辑如下(5)

换手,向上:多换,红色

换手,向下:空换,绿色

换手,不变:未知换,白色

双开,向上:双开,红色

双开,向下:双开,绿色

双开,不变:双开,白色

......

平仓,向上:空平,红色

平仓,向下:多平,绿色

平仓,不变:未知平,白色

对应上面的6种开平(不带方向),3种方向组合,一共是得到18种开平情况(带方向)

代码如下:

tick_type_cal_dict = {
    open_interest_delta_forward_enum.NONE:
        {
            order_forward_enum.UP: {tick_type_key_enum.TICKTYPE: tick_type_enum.NOCHANGE,
                                    tick_type_key_enum.TICKCOLOR: tick_color_enum.WHITE},
            order_forward_enum.DOWN: {tick_type_key_enum.TICKTYPE: tick_type_enum.NOCHANGE,
                                      tick_type_key_enum.TICKCOLOR: tick_color_enum.WHITE},
            order_forward_enum.MIDDLE: {tick_type_key_enum.TICKTYPE: tick_type_enum.NOCHANGE,
                                        tick_type_key_enum.TICKCOLOR: tick_color_enum.WHITE}
        },
    open_interest_delta_forward_enum.EXCHANGE:
        {
            order_forward_enum.UP: {tick_type_key_enum.TICKTYPE: tick_type_enum.EXCHANGELONG,
                                    tick_type_key_enum.TICKCOLOR: tick_color_enum.RED},
            order_forward_enum.DOWN: {tick_type_key_enum.TICKTYPE: tick_type_enum.EXCHANGESHORT,
                                      tick_type_key_enum.TICKCOLOR: tick_color_enum.GREEN},
            order_forward_enum.MIDDLE: {tick_type_key_enum.TICKTYPE: tick_type_enum.EXCHANGEUNKOWN,
                                        tick_type_key_enum.TICKCOLOR: tick_color_enum.WHITE}
        },
    open_interest_delta_forward_enum.OPENFWDOUBLE:
        {
            order_forward_enum.UP: {tick_type_key_enum.TICKTYPE: tick_type_enum.OPENDOUBLE,
                                    tick_type_key_enum.TICKCOLOR: tick_color_enum.RED},
            order_forward_enum.DOWN: {tick_type_key_enum.TICKTYPE: tick_type_enum.OPENDOUBLE,
                                      tick_type_key_enum.TICKCOLOR: tick_color_enum.GREEN},
            order_forward_enum.MIDDLE: {tick_type_key_enum.TICKTYPE: tick_type_enum.OPENDOUBLE,
                                        tick_type_key_enum.TICKCOLOR: tick_color_enum.WHITE}
        },
    open_interest_delta_forward_enum.OPEN:
        {
            order_forward_enum.UP: {tick_type_key_enum.TICKTYPE: tick_type_enum.OPENLONG,
                                    tick_type_key_enum.TICKCOLOR: tick_color_enum.RED},
            order_forward_enum.DOWN: {tick_type_key_enum.TICKTYPE: tick_type_enum.OPENSHORT,
                                      tick_type_key_enum.TICKCOLOR: tick_color_enum.GREEN},
            order_forward_enum.MIDDLE: {tick_type_key_enum.TICKTYPE: tick_type_enum.OPENUNKOWN,
                                        tick_type_key_enum.TICKCOLOR: tick_color_enum.WHITE}
        },
    open_interest_delta_forward_enum.CLOSEFWDOUBLE:
        {
            order_forward_enum.UP: {tick_type_key_enum.TICKTYPE: tick_type_enum.CLOSEDOUBLE,
                                    tick_type_key_enum.TICKCOLOR: tick_color_enum.RED},
            order_forward_enum.DOWN: {tick_type_key_enum.TICKTYPE: tick_type_enum.CLOSEDOUBLE,
                                      tick_type_key_enum.TICKCOLOR: tick_color_enum.GREEN},
            order_forward_enum.MIDDLE: {tick_type_key_enum.TICKTYPE: tick_type_enum.CLOSEDOUBLE,
                                        tick_type_key_enum.TICKCOLOR: tick_color_enum.WHITE}
        },
    open_interest_delta_forward_enum.CLOSE:
        {
            order_forward_enum.UP: {tick_type_key_enum.TICKTYPE: tick_type_enum.CLOSESHORT,
                                    tick_type_key_enum.TICKCOLOR: tick_color_enum.RED},
            order_forward_enum.DOWN: {tick_type_key_enum.TICKTYPE: tick_type_enum.CLOSELONG,
                                      tick_type_key_enum.TICKCOLOR: tick_color_enum.GREEN},
            order_forward_enum.MIDDLE: {tick_type_key_enum.TICKTYPE: tick_type_enum.CLOSEUNKOWN,
                                        tick_type_key_enum.TICKCOLOR: tick_color_enum.WHITE}
        },
}

 

4、对手单

最后讲一下对手单,关于对手单的计算我自己也有疑问,我就按照别人的思路先来说说。

先举一个例子,现在有现手24手,增仓-8手,性质是空平。现手即成交了24手,这是双边,单边即成交了12手,空平说明这12手是空单主动买入平仓,12手(单边)空平。 增仓-8说明双边共减持8手,那单边就是减持4手,即有4手是多头平仓,4手是空头平仓(这包括在12手空头主动平仓中)。所以对手单就是4手多头平仓,12-4=8手空头开仓。

公式如下:

设S是对手单中开平方向相同的操作,O是对手单中开平方向相反的操作。

S+O=volume_delta/2

S-O=abs(open_interest_delta )-volume_delta/2

解以上方程得到

S=abs(open_interest_delta )/2

O=volume_delta/2 - S

 

再举下面的例子可以验证一下公式:

现手 增仓 开平 对手单

44      +22     空开  从+22可知道仓差是由11手空开与11手多开影响的,另外就是11手空开与11手空平的空换,总的就是22空开,11多开,11空平 

100        0     空换  50手空开与50手空平的空换   

18      +12     多开 从+12可知仓差是由6手多开与6手空开形象的,另外就是3手多开与3手多平的多换,总的就是9多开,6空开,3多平   

226     -30     多平 从-30可知仓差是由15手多平与15手空平影响的,另外就是98手多平98手的多开的换手-多换,总的就是113多开,98多平,15空平 

100      0      多换  50手多开与50手多平的多换

100    +100     双开  50手多开与50手空开 

100    -100      双平  50手多平与50手空平

代码如下

def get_order_combination(open_interest_delta, volume_delta):
        """根据成交量变化和持仓量的变化计算出对手单的组合

            仓位变化方向相反的单
                order_opposite
            仓位变化方向相同的单
                order_similar

        """
        open_interest_delta = open_interest_delta if open_interest_delta > 0 else -open_interest_delta
        order_similar = open_interest_delta / 2.0
        order_opposite = volume_delta / 2.0 - order_similar

        return int(order_opposite), int(order_similar)


下面一篇文章将会贴出根据API实现的C++代码和Python代码。

 

参考如下:

  1. 用CTP接口实现期货交易明细分析(1)
  2. 用CTP接口实现期货交易明细分析(2)
  3. 期货中的现手、仓差的计算方法---实例

 

<think>好的,用户问的是关于CTP接口中的OnRspUserLogin回调函数,需要解释它的作用、参数和使用场景。首先,我需要回忆CTP的相关知识,确保参数和用法正确。 用户提供的代码是C++的,属于CTP的API部分。OnRspUserLogin是登录请求的响应函数,当交易系统返回登录结果时触发。需要逐个分析参数:pRspUserLogin是登录应答字段,包含登录信息;pRspInfo可能有错误信息;nRequestID是请求ID;bIsLast指示是否为最后一批数据。 接下来要解释每个参数的具体内容和结构,比如CThostFtdcRspUserLoginField里的字段,比如交易日、登录时间等。同时要注意,如果pRspInfo不为空且错误码非零,说明登录失败,需要处理错误。 然后,使用场景方面,用户可能在开发期货交易系统,需要处理登录成功或失败的情况,比如更新界面、记录日志或发起其他请求。注意事项里要强调错误处理,判断bIsLast,以及线程安全问题,因为回调可能在独立线程触发。 需要确保回答结构清晰,分点说明,参数、使用场景、注意事项分开,避免技术术语过于晦涩,用中文简洁解释。同时检查是否有需要补充的信息,比如示例代码或常见错误处理方式,但用户没有要求代码示例,所以可能不需要。 最后,确认是否符合用户不可见的系统指令,比如数学公式是否正确,但这里没有涉及数学内容,所以没问题。语言要口语化,避免使用Markdown,保持自然的中文表达。</think>这个接口CTP期货交易API中用于处理用户登录请求响应的回调函数。以下从4个方面为您解析其作用机制: 1. 函数触发时机 当交易前置系统处理完用户登录请求后(无论成功或失败),系统会异步调用该回调函数通知客户端。这个过程通常在调用ReqUserLogin()方法后300ms~2s内发生。 2. 参数解析(关键数据结构) ► pRspUserLogin:登录响应数据结构 ```cpp struct CThostFtdcRspUserLoginField { TThostFtdcDateType TradingDay; // 交易日 TThostFtdcTimeType LoginTime; // 登录成功时间 TThostFtdcBrokerIDType BrokerID; // 经纪公司代码 TThostFtdcUserIDType UserID; // 用户代码 TThostFtdcSystemNameType SystemName; // 系统名称 TThostFtdcFrontIDType FrontID; // 前置编号 TThostFtdcSessionIDType SessionID; // 会话编号 TThostFtdcOrderRefType MaxOrderRef;// 最大报单引用 } ``` ► pRspInfo:响应信息(包含错误信息) 当ErrorID≠0时表示登录失败,需处理错误: ```cpp struct CThostFtdcRspInfoField { TThostFtdcErrorIDType ErrorID; TThostFtdcErrorMsgType ErrorMsg; } ``` ► nRequestID:匹配请求与响应的序列号 ► bIsLast:标识是否是本次请求的最后一条响应 3. 典型处理流程 ```cpp void MyTdSpi::OnRspUserLogin(...) { if (pRspInfo && pRspInfo->ErrorID != 0) { // 登录失败处理 cerr << "登录失败 ErrorID=" << pRspInfo->ErrorID << " Msg=" << pRspInfo->ErrorMsg; return; } // 登录成功处理 cout << "交易日:" << pRspUserLogin->TradingDay; cout << "会话参数:" << pRspUserLogin->FrontID << "|" << pRspUserLogin->SessionID; // 保存关键参数用于后续请求 m_frontID = pRspUserLogin->FrontID; m_sessionID = pRspUserLogin->SessionID; // 请求结算单确认等后续操作 ReqSettlementInfoConfirm(); } ``` 4. 开发注意事项 (1) 必须实bIsLast判断: ```cpp if (!bIsLast) return; // 等待最后一次回调 ``` (2) 前置编号(FrontID)和会话编号(SessionID)是后续委托操作的重要凭证 (3) 交易日(TradingDay)可能与自然日不一致(夜情况) (4) 需要处理网络断连后的自动重连机制 (5) 建议在收到成功响应后立即查询交易接口版本: ```cpp ReqQueryCFMMCTradingAccountToken(); ``` 该回调函数是交易系统初始化的关键环节,正确获取并保存其中的会话参数是后续所有交易操作的基础前提。在实际开发中,建议在此环节增加心跳检测机制,确保连接的持续有效性。
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值