记录一些商品期货程序化交易时学习的概念与问题思考,对于初学者入门时概念理解应该会有点帮助。
商品期货行情数据中包含一个OpenInterest
字段的数据,经常有用户咨询这个数据如何获取,获取是非常简单的。在此之前,我们先给还不清楚这个OpenInterest
概念的同学讲解一下,什么是OpenInterest
即:未平仓量。
未平仓量是指市场当时存在的契约口数,举个例子:
如果当前rb2105
螺纹钢2105合约所有多头持有100手,空头也持有100手,未平仓量为100手。此时如果新增一个1手开多仓订单,新增一个1手的开空仓订单,这两个订单撮合成交就形成了一手持仓,未平仓量变动为101手。
|买方|卖方|未平仓量|备注| |-|-|-|-| |开多单|开空单|增加|撮合成交后,形成新增的未平仓量,多空对垒| |开多单|多头平仓单|不变|撮合成交后,多头持仓转移,新多头接盘| |空头平仓单|开空单|不变|撮合成交后,空头持仓转移,新空头接盘| |空头平仓单|多头平仓单|减少|撮合成交后,多头、空头了结合约,平仓离场|
如果多空双方分歧增大,都认为价格将往自己有利的方向进行,可能都会向各自的交易方向上继续加码。反之,如果都认为行情将对于自己不利的方向发展,也都会平仓。未平仓量也会随之下降。
这些心理、预判也可以由一个表格来表达:
|价格指数|未平仓量|成交量|多头|空头|预判|
|-|-|-|-|-|-|
|↑|↑|↑|获利加码|亏损加码|多头稳健|
|↑|↑|↓|获利加码|亏损加码|多方强势|
|↑|↓|↑|获利了结|止损离场|空头回补|
|↑|↓|↓|获利了结|止损离场|涨势趋缓|
|↓|↑|↑|亏损加码|获利加码|空头稳健|
|↓|↑|↓|亏损加码|获利加码|空方强势|
|↓|↓|↑|止损离场|获利了结|多头回补|
|↓|↓|↓|止损离场|获利了结|跌势趋缓|
不过这也仅仅是从盘面数据上对行情做的预判而已,也只是作为行情的参考信息,市场的变化是复杂的、难以捉摸的,对于市场行情的规律来说也不是一成不变的。
用机器人测试时的策略代码:
function main(){
while(true){
if(exchange.IO("status")){
exchange.SetContractType("rb2105")
var ticker = exchange.GetTicker()
Log("ticker.Info:", ticker.Info)
break
} else {
Log(_D(), "未连接CTP !")
}
Sleep(5000)
}
}
拿到数据
{
"HighestPrice": 4447,
"OpenInterest": 1242482, // 持仓量数据
"UpperLimitPrice": 4610,
"AskVolume2": 0,
"ExchangeID": "",
"UpdateMillisec": 500,
"BidVolume2": 0,
"AskPrice3": 1.7976931348623157e+308,
"AskVolume5": 0,
"AveragePrice": 43818.4579350878,
"BidVolume3": 0,
"LastPrice": 4372,
"PreSettlementPrice": 4391,
"PreClosePrice": 4397,
"PreOpenInterest": 1257064,
"Turnover": 90361109400,
"SettlementPrice": 1.7976931348623157e+308,
"BidVolume1": 106,
"AskVolume1": 112,
"TradingDay": "20210106",
"ExchangeInstID": "",
"OpenPrice": 4400,
"LowestPrice": 4315,
"Volume": 2062170,
"LowerLimitPrice": 4171,
"AskPrice2": 1.7976931348623157e+308,
"BidPrice4": 1.7976931348623157e+308,
"ActionDay": "20210106",
"ClosePrice": 1.7976931348623157e+308,
"CurrDelta": 1.7976931348623157e+308,
"AskVolume3": 0,
"BidVolume4": 0,
"AskPrice4": 1.7976931348623157e+308,
"InstrumentID": "rb2105",
"PreDelta": 0,
"BidPrice1": 4371,
"BidPrice2": 1.7976931348623157e+308,
"BidPrice3": 1.7976931348623157e+308,
"BidPrice5": 1.7976931348623157e+308,
"BidVolume5": 0,
"AskPrice5": 1.7976931348623157e+308,
"UpdateTime": "14:43:13",
"AskPrice1": 4372,
"AskVolume4": 0
}
最近有朋友在做商品期货回测的时候有一些困惑,比如在盘口挂一个手数特别大的订单,为什么会一下就成交了,注意说的是回测系统。 所以就举个例子:
/*backtest
start: 2020-12-23 00:00:00
end: 2020-12-24 00:00:00
period: 1h
basePeriod: 1h
exchanges: [{"eid":"Futures_CTP","currency":"FUTURES","balance":100000000000}]
mode: 1
*/
function main(){
while(true){
// 需要在判断exchange.IO("status")函数返回true,即为真值时才可调用行情、交易等函数
if(exchange.IO("status")){
LogStatus(_D(), "已经连接CTP !")
exchange.SetContractType("rb2105")
var ticker = exchange.GetTicker()
Log(ticker)
exchange.SetDirection("buy")
var id = exchange.Buy(ticker.Buy, 10000)
Log("挂单:", exchange.GetOrders())
Log("持仓:", exchange.GetPosition())
while (exchange.GetPosition().length == 0) {
Log(exchange.GetDepth())
}
Log("检测到持仓时的盘口:", exchange.GetDepth())
Log("持仓:", exchange.GetPosition())
throw "stop"
} else {
LogStatus(_D(), "未连接CTP !")
}
Sleep(1000)
}
}
回测运行结果:
可以看到运行日志,开始根据盘口买一的价格下了一个订单,数量是10000手,价格就是当前的买一价格。 但是在一秒之内就全部成交了。打印下一次的行情,可以看到价格已经变为:
检测到持仓时的盘口: {"Asks":[{"Price":4327,"Amount":153}],"Bids":[{"Price":4326,"Amount":50}]}
可以看到卖一价格已经低过了下单的买入价4346。 这个就是回测系统的过价成交机制,可能有的朋友会问了:“为什么你下10000手这么大量的订单,都能完全成交,盘口有那么多量么?” 这种情况是由于这种过价机制的设计,为何要这样设计是因为历史行情回溯的回测系统是不可能完全还原当时行情发生时整个市场的实际交易情况的,这也是常说的回测系统不可能100%模拟还原真实市场的原因之一。
本例子中,盘口挂了一个大额买单,是否挡住了价格从:
2020-12-23 09:00:01 信息 {"Time":1608685201097,"High":4348,"Low":4346,"Sell":4348,"Buy":4346,"Last":4347,"Volume":966074,"OpenInterest":1092818}
下滑到:
检测到持仓时的盘口: {"Asks":[{"Price":4327,"Amount":153}],"Bids":[{"Price":4326,"Amount":50}]}
呢?
显然回测系统不能设计成让这个大额买单挡住下跌,如果挡住了,那么后续的行情就变了,就不是历史行情重放了。所以回测系统要设计成不考虑策略参与交易影响市场,影响行情的模式。这个就是设计为过价成交的原因,所以就有了以上的回测情景。虽然盘口订单量不足,但是价格过了,就默认为有足够的新订单把阻碍行情变动的订单都吃掉了,所以全部成交了。