change.IO("mode", 0)函数使用,这样配合使用就可以使程序在有最新行情时进行响应,执行程序逻辑(使用
exchange.IO("mode", 0)并不影响
exchange.IO("wait"),目的是为了在程序中使用
exchange.GetTicker()等函数调用时不阻塞)。如果
Timeout参数设置
-1该函数设置为立即返回,在没有新事件时返回空值,
Timeout参数设置
0为阻塞等待最新事件,如同不设置
Timeout参数。需要注意的是在使用
exchange.IO("wait")```时,必须至少已经订阅了一个当前处于交易状态的合约(已经交割的过期合约,不会再有行情数据),否则会阻塞在该函数(由于没有任何行情、订单更新)。只支持商品期货实盘。
EventTick
:{Event:"tick", Index:交易所索引, Nano:事件纳秒级时间, Symbol:合约名称, Ticker:行情数据}
。
OrderTick
:{Event:"order", Index:交易所索引, Nano:事件纳秒级时间, Order:订单信息}
。
简单实现回调机制:
function on_tick(symbol, ticker) {
Log("symbol:", symbol, "update")
Log("ticker:", ticker)
}
function on_order(order) {
Log("order update", order)
}
function main() {
while(!exchange.IO("status")) {
Sleep(10)
}
// 如果在程序中不使用诸如exchange.GetTicker()之类的获取行情的函数,可以不设置exchange.IO("mode", 0)
exchange.IO("mode", 0)
_C(exchange.SetContractType, "MA005")
while(true) {
var e = exchange.IO("wait")
if(e) {
if(e.Event == "tick") {
on_tick(e.Symbol, e.Ticker)
} else if(e.Event == "order") {
on_order(e.Order)
}
}
}
}
def on_tick(symbol, ticker):
Log("symbol:", symbol, "update")
# 数据结构:https://www.youquant.com/api#ticker
Log("ticker:", ticker)
def on_order(order):
Log("order update", order)
def main():
# wait connect trade server
while not exchange.IO("status"):
Sleep(10)
# switch push mode
exchange.IO("mode", 0)
# subscribe instrument
_C(exchange.SetContractType, "MA001")
while True:
e = exchange.IO("wait")
if e:
if e.Event == "tick":
on_tick(e['Symbol'], e['Ticker'])
elif e.Event == "order":
on_order(e['Order'])
void on_tick(const string &symbol, json &ticker) {
Log("symbol:", symbol, "update");
Log("ticker:", ticker);
}
void on_order(json &order) {
Log("order update", order);
}
void main() {
while(exchange.IO("status") == 0) {
Sleep(10);
}
exchange.IO("mode", 0);
_C(exchange.SetContractType, "rb2005");
while(true) {
auto e = exchange.IO("wait");
if(e != false) {
if(e["Event"] == "tick") {
on_tick(e["Symbol"], e["Ticker"]);
}
else if(e["Event"] == "order") {
on_order(e["Order"]);
}
}
}
}
多品种回调例子:
function on_tick(symbol, ticker) {
Log("symbol:", symbol, "update", "ticker:", ticker)
}
function main() {
while(!exchange.IO("status")) {
Sleep(10)
}
_C(exchange.SetContractType, "MA101")
_C(exchange.SetContractType, "rb2101")
_C(exchange.SetContractType, "i2101")
while(true) {
var e = exchange.IO("wait", -1)
if(e) {
if(e.Event == "tick") {
on_tick(e.Symbol, e.Ticker)
}
}
Sleep(10)
}
}
def on_tick(symbol, ticker):
Log("symbol:", symbol, "update", "ticker:", ticker)
def main():
while not exchange.IO("status"):
Sleep(10)
_C(exchange.SetContractType, "MA101")
_C(exchange.SetContractType, "rb2101")
_C(exchange.SetContractType, "i2101")
while True:
e = exchange.IO("wait", -1)
if e:
if e.Event == "tick":
on_tick(e['Symbol'], e['Ticker'])
Sleep(10)
void on_tick(const string &symbol, json &ticker) {
Log("symbol:", symbol, "update", "ticker:", ticker);
}
void main() {
while(exchange.IO("status") == 0) {
Sleep(10);
}
_C(exchange.SetContractType, "MA101");
_C(exchange.SetContractType, "rb2101");
_C(exchange.SetContractType, "i2101");
while(true) {
auto e = exchange.IO("wait", -1);
if(e != false) {
if(e["Event"] == "tick") {
on_tick(e["Symbol"], e["Ticker"]);
}
}
}
}
使用wait_any
参数,设置阻塞:
exchange.IO("wait_any")
,作用同上exchange.IO("wait", Timeout)
。但是指的是有任何一个交易所对象收到行情信息时就返回,EventTick
里的Index
指交易所索引。用于多交易所对象(多个期货公司前置服务器)收集行情数据的场景,只支持商品期货实盘。
使用instruments
参数,获取所有合约的列表数据:
exchange.IO("instruments")
,返回交易所所有合约的列表,只支持实盘。
function main() {
while (!exchange.IO("status")) {
LogStatus("正在等待与交易服务器连接, " + new Date())
}
Log("开始获取所有合约")
var instruments = _C(exchange.IO, "instruments")
Log("合约列表获取成功")
var len = 0
for (var instrumentId in instruments) {
len++
}
Log("合约列表长度为:",len)
}
def main():
while not exchange.IO("status"):
LogStatus("正在等待与交易服务器连接, " + _D())
Log("开始获取所有合约")
instruments = _C(exchange.IO, "instruments")
Log("合约列表获取成功")
length = 0
for i in range(len(instruments)):
length += 1
Log("合约列表长度为:", length)
void main() {
while(exchange.IO("status") == 0) {
LogStatus("正在等待与交易服务器连接, " + _D());
}
Log("开始获取所有合约");
auto instruments = _C(exchange.IO, "instruments");
Log("合约列表获取成功");
int length = 0;
for(int i = 0; i < instruments.size(); i++) {
length++;
}
Log("合约列表长度为:", length);
}
使用products
参数,获取所有产品的列表数据:
exchange.IO("products")
,返回交易所所有产品的列表,只支持实盘。
使用subscribed
参数,获取已经订阅的合约数据:
exchange.IO("subscribed")
,返回已订阅行情的合约,只支持实盘。
使用settlement
参数,获取结算单数据:
exchange.IO("settlement")
,结算单查询,不加第二个参数默认返回之前一个交易日的数据,加参数如20170317
指返回日期为2017-03-17的结算单,只支持实盘。
使用api
参数,调用底层接口:
优宽量化的CTP(商品期货)终端提供了完整的全API实现,当优宽平台的API满足不了你需要的功能时可以用exchange.IO
函数进行更深层的系统调用,完全兼容官方的Api名称。CTP的IO直接扩展函数调用请求,将会在收到第一个isLast
标记为true
的响应包后返回。
CTP协议接口:CTP协议接口相关资料
以几个简单的例子做为说明:
查询投资者信息
function main() {
while (!exchange.IO("status")) {
LogStatus("正在等待与交易服务器连接, " + new Date())
}
Log(exchange.IO("api", "ReqQryInvestor"))
}
def main():
while not exchange.IO("status"):
LogStatus("正在等待与交易服务器连接, " + _D())
Log(exchange.IO("api", "ReqQryInvestor"))
void main() {
while(exchange.IO("status") == 0) {
LogStatus("正在等待与交易服务器连接, " + _D());
}
Log(exchange.IO("api", "ReqQryInvestor"));
}
修改密码
function main() {
// CTP协议建立连接时需要时间
Sleep(6000)
exchange.IO("api", "ReqUserPasswordUpdate", {BrokerID: "9999", UserID: "11111", OldPassword: "oldpass", NewPassword: "newpass"})
}
def main():
Sleep(6000)
exchange.IO("api", "ReqUserPasswordUpdate", {"BrokerID": "9999", "UserID": "11111", "OldPassword": "oldpass", "NewPassword": "newpass"})
void main() {
Sleep(6000);
exchange.IO("api", "ReqUserPasswordUpdate", R"({"BrokerID": "9999", "UserID": "11111", "OldPassword": "oldpass", "NewPassword": "newpass"})"_json);
}
复杂的例子
function main() {
// CTP协议建立连接时需要时间
Sleep(6000)
// 如果再加一个参数值为false表示不等待返回值,只发送请求,第三个参数只需要填充需要的字段,也可省略此参数,如果类型为char,传长度为1的字符串即可
var r = exchange.IO("api", "ReqQryProduct", {ProductID: "MA"})
// CTP未登陆的时候会失败
if (!r) {
return
}
_.each(r, function(item) {
// IO请求可能返回多个数据包,所以以数组的形式返回。便历数据包的所有数据类型,一个数据包可能包含多个具体数据,具体数据类型的名称,请参看CTP官方文档http://www.sfit.com.cn/5_2_DocumentDown.htm
_.each(item, function(f) {
// 取出来需要的数据,Name为此数据的类型,Value为此数据的值
if (f.Name == 'CThostFtdcProductField') {
// 打印查询的的甲醇的信息
Log(f.Value)
}
})
});
}
def main():
Sleep(6000)
r = exchange.IO("api", "ReqQryProduct", {"ProductID": "MA"})
if not r:
return
for r_index in range(len(r)):
for f_index in range(len(r[r_index])):
if r[r_index][f_index]["Name"] == 'CThostFtdcProductField':
Log(r[r_index][f_index]["Value"])
void main() {
Sleep(6000);
auto r = exchange.IO("api", "ReqQryProduct", R"({"ProductID": "MA"})"_json);
if(r == false) {
return;
}
for(auto& ele1 : r.items()) {
for(auto& ele2 : ele1.value().items()) {
if(ele2.value()["Name"] == "CThostFtdcProductField") {
Log(ele2.value()["Value"]);
}
}
}
}
查询结算单
function main() {
while (!exchange.IO("status")) {
LogStatus("正在等待与交易服务器连接, " + new Date())
}
// 也可不指定日期
var r = exchange.IO("api", "ReqQrySettlementInfo", {TradingDay: "20190506"})
var s = ''
_.each(r, function(item) {
_.each(item, function(f) {
if (f.Name == 'CThostFtdcSettlementInfoField') {
s += f.Value.Content
}
})
})
Log(s)
}
def main():
while not exchange.IO("status"):
LogStatus("正在等待与交易服务器连接, " + _D())
r = exchange.IO("api", "ReqQrySettlementInfo", {"TradingDay": "20190506"})
s = ''
for i in range(len(r)):
for ii in range(len(r[i])):
if r[i][ii]["Name"] == "CThostFtdcSettlementInfoField":
s += r[i][ii]["Value"]["Content"]
Log(s)
void main() {
while(exchange.IO("status") == 0) {
LogStatus("正在等待与交易服务器连接, " + _D());
}
auto r = exchange.IO("api", "ReqQrySettlementInfo", R"({"TradingDay": "20200311"})"_json);
string s = "";
for(auto& ele1 : r.items()) {
for(auto& ele2 : ele1.value().items()) {
if(ele2.value()["Name"] == "CThostFtdcSettlementInfoField") {
s += std::string(ele2.value()["Value"]["Content"]);
}
}
}
Log(s);
}
exchange.Log(LogType, Price, Amount)
,该函数调用时不下单, 只记录交易信息,用于输出交易日志信息。
注意:
exchange
交易所对象的成员函数,区别于全局函数Log()
。LogType
可为LOG_TYPE_BUY
,LOG_TYPE_SELL
,LOG_TYPE_CANCEL
,Price
为价格,Amount
为数量,当LogType
为LOG_TYPE_CANCEL
的时候Price
参数为订单ID。使用exchange.Log(LogType, Price, Amount)
可以进行实盘跟单测试、模拟下单、可以辅助记录下单。
var id = 123
function main() {
// 下单类型买入,价格999,数量 0.1
exchange.Log(LOG_TYPE_BUY, 999, 0.1)
// 取消订单
exchange.Log(LOG_TYPE_CANCEL, id)
}
id = 123
def main():
exchange.Log(LOG_TYPE_BUY, 999, 0.1)
exchange.Log(LOG_TYPE_CANCEL, id)
void main() {
auto id = 123;
exchange.Log(LOG_TYPE_BUY, 999, 0.1);
exchange.Log(LOG_TYPE_CANCEL, id);
}
exchange.GetAccount()
,将返回交易所账户信息。返回值:Account
结构结构体。
function main(){
// 鉴于测试代码,不使用商品期货策略一般架构,这里仅仅判断exchange.IO("status")函数,判断连接期货公司前置机成功后立即执行测试代码。股票证券无需使用exchange.IO("status")判断连接状态
while (!exchange.IO("status")) {
Sleep(1000)
}
// 获取账户资产信息,可以不用设置合约
var account = exchange.GetAccount()
Log("账户信息,Balance:", account.Balance, "FrozenBalance:", account.FrozenBalance, "Stocks:",
account.Stocks, "FrozenStocks:", account.FrozenStocks)
}
def main():
while not exchange.IO("status"):
Sleep(1000)
account = exchange.GetAccount()
Log("账户信息,Balance", account["Balance"], "FrozenBalance:", account["FrozenBalance"], "Stocks:",
account["Stocks"], "FrozenStocks:", account["FrozenStocks"])
void main() {
while (exchange.IO("status") == 0) {
Sleep(1000);
}
auto account = exchange.GetAccount();
Log("账户信息,Balance", account.Balance, "FrozenBalance:", account.FrozenBalance, "Stocks:",
account.Stocks, "FrozenStocks:", account.FrozenStocks);
}
exchange.GetName()
,返回交易所名称。返回值:字符串类型。
命令行版本的托管者,可以使用-l
命令打印交易所名称列表。
股票证券
实盘:富途证券交易所对象时exchange.GetName()
函数返回Futures_Futu
。中泰XTP交易所对象时exchange.GetName()
函数返回Futures_XTP
。
回测系统:回测系统并不区分协议,所以exchange.GetName()
函数返回Futures_XTP
。
商品期货
实盘:CTP协议的交易所对象时exchange.GetName()
函数返回Futures_CTP
,易盛协议的交易所对象时exchange.GetName()
函数返回Futures_Esunny
。
回测系统:回测系统并不区分协议,所以exchange.GetName()
函数返回Futures_CTP
。
exchange.GetLabel()
,返回交易所自定义的标签(在优宽上配置账户信息的页面设置)。返回值:字符串类型。
exchange.GetCurrency()
,商品期货、股票证券等返回固定的字符串。返回值:字符串类型。
股票证券
实盘:富途证券交易所对象时exchange.GetCurrency()
函数返回STOCK
。
回测系统:exchange.GetCurrency()
函数返回STOCK_CNY
。
商品期货
实盘:exchange.GetCurrency()
函数返回FUTURES
。
回测系统:exchange.GetCurrency()
函数返回FUTURES_CNY
。
exchange.GetQuoteCurrency()
,返回交易所操作的基础货币名称。返回值:字符串类型。例如国内商品期货的交易所对象返回CNY
。
期货支持传统商品期货CTP协议,易盛协议。所有操作前需要先使用exchange.SetContractType
函数设置合约代码。
对于商品期货合约代码不太熟悉的用户可以使用如下JavaScript
语言的代码查询:
function main(){
while(true){
if(exchange.IO("status")){
var products_CZCE_Tbl = {
"type" : "table",
"title" : "郑商所 CZCE",
"cols" : ["商品名称(ProductName)", "合约代码短名(ProductID)" , "一跳价格(PriceTick)", "一手合约乘数(VolumeMultiple)", "交易所代码(ExchangeID)"],
"rows" : []
}
var products_DCE_Tbl = {
"type" : "table",
"title" : "大商所 DCE",
"cols" : ["商品名称(ProductName)", "合约代码短名(ProductID)" , "一跳价格(PriceTick)", "一手合约乘数(VolumeMultiple)", "交易所代码(ExchangeID)"],
"rows" : []
}
var products_SHFE_Tbl = {
"type" : "table",
"title" : "上期所 SHFE",
"cols" : ["商品名称(ProductName)", "合约代码短名(ProductID)" , "一跳价格(PriceTick)", "一手合约乘数(VolumeMultiple)", "交易所代码(ExchangeID)"],
"rows" : []
}
var products_other_Tbl = {
"type" : "table",
"title" : "其它",
"cols" : ["商品名称(ProductName)", "合约代码短名(ProductID)" , "一跳价格(PriceTick)", "一手合约乘数(VolumeMultiple)", "交易所代码(ExchangeID)"],
"rows" : []
}
exchange.IO("products").forEach(function(product) {
if (product.ExchangeID == "CZCE") {
products_CZCE_Tbl.rows.push([product.ProductName, product.ProductID, product.PriceTick, product.VolumeMultiple, product.ExchangeID])
} else if (product.ExchangeID == "DCE") {
products_DCE_Tbl.rows.push([product.ProductName, product.ProductID, product.PriceTick, product.VolumeMultiple, product.ExchangeID])
} else if (product.ExchangeID == "SHFE") {
products_SHFE_Tbl.rows.push([product.ProductName, product.ProductID, product.PriceTick, product.VolumeMultiple, product.ExchangeID])
} else {
products_other_Tbl.rows.push([product.ProductName, product.ProductID, product.PriceTick, product.VolumeMultiple, product.ExchangeID])
}
})
LogStatus(_D(), "已经连接CTP", "\n`" + JSON.stringify([products_CZCE_Tbl, products_DCE_Tbl, products_SHFE_Tbl, products_other_Tbl]) + "`")
Sleep(1000 * 60 * 5)
} else {
LogStatus(_D(), "未连接CTP !")
}
Sleep(1000)
}
}
查询结果显示各个交易所的合约信息
商品期货合约命名规则如下: 前面字母代表品种名(合约代码短名),后面数字代表合约到期日。
上期 / 能源所:小写 + 4个数字 大商所:小写 + 4个数字 中金所:大写 + 4个数字 郑商所:大写 + 3个数字
exchange.GetPosition()
,获取当前持仓信息。返回值:position
结构体数组。没有持仓则返回空数组,即[]。
/*
注意:GetPosition函数获取的是所有持仓品种的持仓信息,如果没有持仓则返回空数组,所以使用该接口返回的数据前要先判断返回的数据是否为空数组
*/
function main(){
// 鉴于测试代码,不使用商品期货策略一般架构,这里仅仅判断exchange.IO("status")函数,判断连接期货公司前置机成功后立即执行测试代码。股票证券无需使用exchange.IO("status")判断连接状态
while(!exchange.IO("status")) {
Sleep(1000)
}
var info = exchange.SetContractType("rb888")
var ticker = exchange.GetTicker()
exchange.SetDirection("buy")
exchange.Buy(ticker.Last + info.PriceTick * 20, 2)
var position = exchange.GetPosition()
if(position.length>0){
Log("Amount:", position[0].Amount, "FrozenAmount:", position[0].FrozenAmount, "Price:",
position[0].Price, "Profit:", position[0].Profit, "Type:", position[0].Type,
"ContractType:", position[0].ContractType)
}
}
def main():
while not exchange.IO("status"):
Sleep(1000)
info = exchange.SetContractType("rb888")
ticker = exchange.GetTicker()
exchange.SetDirection("buy")
exchange.Buy(ticker["Last"] + info["PriceTick"] * 20, 2)
position = exchange.GetPosition()
if len(position) > 0:
Log("Amount:", position[0]["Amount"], "FrozenAmount:", position[0]["FrozenAmount"], "Price:",
position[0]["Price"], "Profit:", position[0]["Profit"], "Type:", position[0]["Type"],
"ContractType:", position[0]["ContractType"])
void main() {
while(exchange.IO("status") == 0) {
Sleep(1000);
}
auto info = exchange.SetContractType("rb888");
auto ticker = exchange.GetTicker();
exchange.SetDirection("buy");
exchange.Buy(ticker.Last + info["PriceTick"].get<double>() * 20, 2);
auto position = exchange.GetPosition();
if(position.size() > 0) {
Log("Amount:", position[0].Amount, "FrozenAmount:", position[0].FrozenAmount, "Price:",
position[0].Price, "Profit:", position[0].Profit, "Type:", position[0].Type,
"ContractType:", position[0].ContractType);
}
}
商品期货的持仓需要注意:
回测系统
回测系统不区分今仓、昨仓。GetPosition
函数返回的持仓数据Position
结构数组中,Position
结构的Type
属性仅为PD_LONG
或者PD_SHORT
。
实盘
有交易所区分今仓、昨仓,例如上期所。
有些交易所虽然也区分,但是不能指定平今还是平昨,有今仓优先平今仓。例如IF
等一些品种只能先平今仓,所以今仓、昨仓仓位信息合并为一个并且不予区分。
exchange.SetMarginLevel(MarginLevel)
,设置杆杠大小。参数值:数值类型。商品期货、股票证券不支持。
exchange.SetDirection(Direction)
,设置exchange.Buy
或者exchange.Sell
函数进行期货下单的方向。参数值:字符串类型。
SetDirection
函数设置期货交易的方向和下单函数之间的对应关系:
下单函数 | SetDirection函数的参数设置的方向 | 备注 |
---|---|---|
exchange.Buy | “buy” | 买入开多仓 |
exchange.Buy | “closesell” | 买入平空仓 |
exchange.Buy | “closesell_today” | 买入平空仓(今仓) |
exchange.Sell | “sell” | 卖出开空仓 |
exchange.Sell | “closebuy” | 卖出平多仓 |
exchange.Sell | “closebuy_today” | 卖出平多仓(今仓) |
参数Direction
可以取buy
,closebuy
,sell
,closesell
,closebuy_today
,closesell_today
。其中closebuy_today
,closesell_today
指平今仓。closebuy
/closesell
为平昨仓。对于CTP协议传统期货,可以设置第二个参数"1"或者"2"或者"3",分别指"投机",“套利”,“套保”,不设置默认为投机。
function main(){
// 鉴于测试代码,不使用商品期货策略一般架构,这里仅仅判断exchange.IO("status")函数,判断连接期货公司前置机成功后立即执行测试代码。股票证券无需使用exchange.IO("status")判断连接状态
while(!exchange.IO("status")) {
Sleep(1000)
}
// 举例设置为螺纹钢主力合约
exchange.SetContractType("rb888")
// 设置下单类型为做多
exchange.SetDirection("buy")
// 以10000的价格,合约数量为2张下单。注意,这里的下单价格为举例,具体测试的时候可以自行设置、改动
exchange.Buy(10000, 2)
// 设置下单类型为平多
exchange.SetDirection("closebuy")
exchange.Sell(1000, 2)
}
def main():
while not exchange.IO("status"):
Sleep(1000)
exchange.SetContractType("rb888")
exchange.SetDirection("buy")
exchange.Buy(10000, 2)
exchange.SetDirection("closebuy")
exchange.Sell(1000, 2)
void main() {
while(exchange.IO("status") == 0) {
Sleep(1000);
}
exchange.SetContractType("rb888");
exchange.SetDirection("buy");
exchange.Buy(10000, 2);
exchange.SetDirection("closebuy");
exchange.Sell(1000, 2);
}
exchange.SetContractType(ContractType)
,设置合约类型。参数值:字符串类型。
商品期货的ContractType
就是指的合约代码。例如exchange.SetContractType("au1506")
,返回合约的详细信息,如最少一次买多少、手续费、交割时间等数据。主力连续合约的代码为888
,例如MA888
,连续指数合约为000
,例如MA000
。888
与000
虚拟合约的交易只支持回测,实盘只支持获取行情。
在商品期货策略中调用exchange.SetContractType(ContractType)
函数时,实盘或者simnow
模拟盘中是可能订阅失败的,例如连接期货公司前置机失败时或者设置了不存在的合约代码时。订阅虚拟合约成功以后,返回的字段里面的InstrumentID
是主力合约(会在订阅同时获取),方便策略实盘下单交易时做映射使用。
使用exchange.SetContractType(ContractType)
函数订阅合约以后,返回商品期货合约的详细信息:
function main(){
while(true) {
if (exchange.IO("status")) {
var ret = exchange.SetContractType("MA888")
Log("订阅的合约的详细信息:", ret)
break
} else {
LogStatus(_D(), "未连接")
}
}
}
def main():
while True:
if exchange.IO("status"):
ret = exchange.SetContractType("MA888")
Log("订阅的合约的详细信息:", ret)
break
else:
LogStatus(_D(), "未连接")
void main() {
while(true) {
if(exchange.IO("status") == 1) {
auto ret = exchange.SetContractType("MA888");
Log("订阅的合约的详细信息:", ret);
break;
} else {
LogStatus(_D(), "未连接");
}
}
}
JavaScript
语言的策略打印ret
变量,即合约详细信息如下:
{
"InstrumentName": "甲醇连续",
"MinLimitOrderVolume": 1,
"OpenDate": "20190116",
"PositionType": 50,
"LongMarginRatio": 0.06999999999999999,
"DeliveryYear": 2020,
"MaxMarketOrderVolume": 1000,
"ExpireDate": "20200114",
"PositionDateType": 50,
"InstLifePhase": 49,
"UnderlyingMultiple": 1,
"CombinationType": 48,
"InstrumentID": "MA001",
"ExchangeInstID": "MA001",
"ProductClass": 49,
"MinMarketOrderVolume": 1,
"VolumeMultiple": 10,
"CreateDate": "20190116",
"ShortMarginRatio": 0.06999999999999999,
"UnderlyingInstrID": "",
"ProductID": "MA",
"PriceTick": 1,
"StartDelivDate": "20200114",
"EndDelivDate": "20200114",
"ExchangeID": "CZCE",
"MaxLimitOrderVolume": 1000,
"MaxMarginSideAlgorithm": 48,
"DeliveryMonth": 1,
"IsTrading": 1,
"StrikePrice": 0,
"OptionsType": 0
}
商品期货只有回测支持虚拟合约交易,实盘时虚拟合约只支持获取行情,实盘时我们可以用虚拟合约映射的真实合约下单,例如以下代码:
function main(){
var n = 0
while(true){
// 需要在判断exchange.IO("status")函数返回true,即为真值时才可调用行情、交易等函数
if(exchange.IO("status")){
// 设置合约为虚拟合约,MA888,即甲醇主力合约
var ret = _C(exchange.SetContractType, "MA888")
var ticker = exchange.GetTicker()
// 当到达交易条件时
if(n == 100) {
exchange.SetContractType(ret.InstrumentID)
Log("设置映射的实际合约:", ret.InstrumentID)
exchange.SetDirection("buy")
var id = exchange.Buy(ticker.Buy - 10, 1)
Log("id:", id)
Sleep(1000)
Log(exchange.GetOrder(id))
Sleep(1000)
Log(exchange.GetPosition())
Sleep(1000)
exchange.CancelOrder(id)
Sleep(1000)
Log(exchange.GetOrders())
}
n++
LogStatus(_D(), "已经连接CTP !")
} else {
LogStatus(_D(), "未连接CTP !")
}
}
}
def main():
n = 0
while True:
if exchange.IO("status"):
ret = _C(exchange.SetContractType, "MA888")
ticker = exchange.GetTicker()
if n == 100:
exchange.SetContractType(ret["InstrumentID"])
Log("设置映射的实际合约:", ret["InstrumentID"])
exchange.SetDirection("buy")
id = exchange.Buy(ticker["Buy"] - 10, 1)
Log("id:", id)
Sleep(1000)
Log(exchange.GetOrder(id))
Sleep(1000)
Log(exchange.GetPosition())
Sleep(1000)
exchange.CancelOrder(id)
Sleep(1000)
Log(exchange.GetOrders())
n += 1
LogStatus(_D(), "已经连接CTP !")
else:
LogStatus(_D(), "未连接CTP !")
void main() {
int n = 0;
while(true) {
if(exchange.IO("status") == 1) {
auto ret = exchange.SetContractType("MA888");
auto ticker = exchange.GetTicker();
if(n == 100) {
exchange.SetContractType(ret["InstrumentID"]);
Log("设置映射的实际合约:", ret["InstrumentID"]);
exchange.SetDirection("buy");
auto id = exchange.Buy(ticker.Buy - 10, 1);
Log("id:", id);
Sleep(1000);
Log(exchange.GetOrder(id));
Sleep(1000);
Log(exchange.GetPosition());
Sleep(1000);
exchange.CancelOrder(id);
Sleep(1000);
Log(exchange.GetOrders());
}
n++;
LogStatus(_D(), "已经连接CTP !");
} else {
LogStatus(_D(), "未连接CTP !");
}
}
}
exchange.GetContractType()
,返回交易所对象(exchange
)当前设置的合约代码,返回值:字符串。
function main () {
// 鉴于测试代码,不使用商品期货策略一般架构,这里仅仅判断exchange.IO("status")函数,判断连接期货公司前置机成功后立即执行测试代码。股票证券无需使用exchange.IO("status")判断连接状态
while(!exchange.IO("status")) {
Sleep(1000)
}
Log(exchange.SetContractType("rb888"))
Log(exchange.GetContractType())
}
def main():
while not exchange.IO("status"):
Sleep(1000)
Log(exchange.SetContractType("rb888"))
Log(exchange.GetContractType())
void main() {
while(exchange.IO("status") == 0) {
Sleep(1000);
}
Log(exchange.SetContractType("rb888"));
Log(exchange.GetContractType());
}
StrDecode()
,商品期货CTP协议中的汉字是GBK编码可用此函数解码。
传统商品期货API获取更多信息,需要注意策略程序启动运行时需要确保和期货公司前置机服务器连接成功后(账号登录验证成功与否通过exchange.IO("status")
函数调用时的返回值判断),才可以调用行情、交易等接口与期货公司前置机服务器通信。参考:exchange.IO(...)函数
function main() {
while (!exchange.IO("status")) {
LogStatus("正在等待与交易服务器连接, " + new Date())
Sleep(1000)
}
exchange.GetAccount()
// 在GetAccount成功后调用,获取更详细的账户信息,可以用JSON.parse解析
Log(exchange.GetRawJSON())
}
def main():
while not exchange.IO("status"):
LogStatus("正在等待与交易服务器连接, " + _D())
Sleep(1000)
exchange.GetAccount()
Log(exchange.GetRawJSON())
void main() {
while(exchange.IO("status") == 0) {
LogStatus("正在等待与交易服务器连接, " + _D());
Sleep(1000);
}
exchange.GetAccount();
// C++ 不支持exchange.GetRawJSON()函数
}
同样支持exchange.GetTicker()
,exchange.GetDepth()
,exchange.GetPosition()
,exchange.GetOrders()
,exchange.GetOrder(Id)
调用后,调用exchange.GetRawJSON()
。
期货交易中exchange.Buy(Price, Amount)
,exchange.Sell(Price, Amount)
,exchange.CancelOrder(Id)
和现货交易的区别:
exchange.Buy(Price, Amount)
或exchange.Sell(Price, Amount)
调用之前需要调用exchange.SetDirection(Direction)
明确下单方向。
function main() {
while (!exchange.IO("status")) {
LogStatus("正在等待与交易服务器连接, " + new Date())
Sleep(1000)
}
// 设置合约
exchange.SetContractType("MA888")
// 设置为开空头仓位,调用exchange.Sell(...)函数下单开空头仓位
exchange.SetDirection("sell")
var id = exchange.Sell(1000, 2)
exchange.CancelOrder(id)
}
def main():
while not exchange.IO("status"):
LogStatus("正在等待与交易服务器连接, " + _D())
Sleep(1000)
exchange.SetContractType("MA888")
exchange.SetDirection("sell")
id = exchange.Sell(1000, 2)
exchange.CancelOrder(id)
void main() {
while(exchange.IO("status") == 0) {
LogStatus("正在等待与交易服务器连接, " + _D());
Sleep(1000);
}
exchange.SetContractType("MA888");
exchange.SetDirection("sell");
auto id = exchange.Sell(1000, 2);
exchange.CancelOrder(id);
}
商品期货支持自定义订单类型(支持实盘,回测暂不支持)。
以后缀方式指定,附加在_
后面例如:
CTP协议:
exchange.SetDirection("buy_ioc")
交易所暂不支持这种类型:exchange.SetDirection("sell_gtd-20170111")
。
CTP协议:
后缀 | 意义 | 对应CTP原始值 |
---|---|---|
ioc | 立即完成,否则撤销 | THOST_FTDC_TC_IOC |
gfs | 本节有效 | THOST_FTDC_TC_GFS |
gfd | 当日有效 | THOST_FTDC_TC_GFD |
gtd | 指定日期前有效 | THOST_FTDC_TC_GTD |
gtc | 撤销前有效 | THOST_FTDC_TC_GTC |
gfa | 集合竞价有效 | THOST_FTDC_TC_GFA |
fak | 部分成交,撤销剩余部分 | THOST_FTDC_TC_IOC |
fok | 未能完全成交,全部撤销 | THOST_FTDC_TC_IOC |
易盛协议:
后缀 | 意义 | 对应易盛协议原始值 |
---|---|---|
gfd | 当日有效 | TAPI_ORDER_TIMEINFORCE_GFD |
gtc | 撤销前有效 | TAPI_ORDER_TIMEINFORCE_GTC |
gtd | 指定日期前有效 | TAPI_ORDER_TIMEINFORCE_GTD |
fak | 部分成交,撤销剩余部分 | TAPI_ORDER_TIMEINFORCE_FAK |
ioc | 立即完成,否则撤销 | TAPI_ORDER_TIMEINFORCE_FAK |
fok | 未能完全成交,全部撤销 | TAPI_ORDER_TIMEINFORCE_FOK |
易盛协议
exchange.GetOrder(id)
、exchange.GetOrders()
、exchange.GetPosition()
、exchange.GetAccount()
接口时立即返回。这些接口在交易所对象配置为易盛协议时,是立即返回的,本地维护数据,由期货公司前置机推送数据更新,不会阻塞。期货交易所对象相关的报错信息说明如下表:
值 | 错误函数显示 | 触发函数 | 描述 |
---|---|---|---|
0 | Futures_OP 0 | exchange.SetMarginLevel | 调用杠杆函数时报错,商品期货不支持SetMarginLevel函数,所以不会报Futures_OP 0错误 |
1 | Futures_OP 1 | exchange.SetDirection | 设置期货交易方向函数报错 |
2 | Futures_OP 2 | exchange.SetContractType | 设置合约函数报错 |
3 | Futures_OP 3 | exchange.GetPosition | 获取持仓函数报错 |
4 | Futures_OP 4 | exchange.IO | 调用IO函数报错 |
需要期货公司开通相关权限,期权合约的代码形式,例如铁矿石期权:i2106-P-760
。
商品期货期权合约基本格式:
标的期货合约 + 看涨 / 看跌 + 行权价
由于交易所的标准合约命名规则并不相同,且对大小写敏感,各交易所合约格式可能有差别,以下是具体交易所对应的合约格式。
上期所:小写 + 四个数字 +
C
(或者P
) + 行权价 郑商所:大写 + 三个数字 +C
(或者P
) + 行权价 中金所:大写 + 四个数字 +-C-
(或者-P-
) + 行权价 大商所:小写 + 四个数字 +-C-
(或者-P-
) + 行权价
交易所期权合约代码示例:
交易所 | 合约名称 | 看涨期权 | 看跌期权 | 看涨期权示例 | 看跌期权示例 |
---|---|---|---|---|---|
中金所 | 沪深300 | IO合约月份-C-行权价格 | IO合约月份-P-行权价格 | IO2105-C-6400 | IO2105-P-6400 |
上期所 | 铜 | cu合约月份C行权价格 | cu合约月份P行权价格 | cu2106C69000 | cu2106P69000 |
上期所 | 黄金 | au合约月份C行权价格 | au合约月份P行权价格 | au2106C400 | au2106P400 |
上期所 | 铝 | al合约月份C行权价格 | al合约月份P行权价格 | al2106C20000 | al2106P20000 |
上期所 | 锌 | zn合约月份C行权价格 | zn合约月份P行权价格 | zn2106C23600 | zn2106P23600 |
上期所 | 橡胶 | ru合约月份C行权价格 | ru合约月份P行权价格 | ru2109C16000 | ru2109P16000 |
大商所 | 豆粕 | m合约月份-C-行权价格 | m合约月份-P-行权价格 | m2109-C-4000 | m2109-P-4000 |
大商所 | 玉米 | c合约月份-C-行权价格 | c合约月份-P-行权价格 | c2109-C-3000 | c2109-P-3000 |
大商所 | 铁矿 | i合约月份-C-行权价格 | i合约月份-P-行权价格 | i2109-C-1000 | i2109-P-1000 |
大商所 | 液化石油气 | pg合约月份-C-行权价格 | pg合约月份-P-行权价格 | pg2106-C-4000 | pg2106-P-4000 |
大商所 | PP | pp合约月份-C-行权价格 | pp合约月份-P-行权价格 | pp2109-C-9000 | pp2109-P-9000 |
大商所 | PVC | v合约月份-C-行权价格 | v合约月份-P-行权价格 | v2109-C-9000 | v2109-P-9000 |
大商所 | 塑料 | l合约月份-C-行权价格 | l合约月份-P-行权价格 | l2109-C-9000 | l2109-P-9000 |
郑商所 | 白糖 | SR合约月份C行权价格 | SR合约月份P行权价格 | SR109C5000 | SR109P5000 |
郑商所 | 棉花 | CF合约月份C行权价格 | CF合约月份P行权价格 | CF109C15000 | CF109P15000 |
郑商所 | PTA | TA合约月份C行权价格 | TA合约月份P行权价格 | TA109C5000 | TA109P5000 |
郑商所 | 甲醇 | MA合约月份C行权价格 | MA合约月份P行权价格 | MA109C2500 | MA109P2500 |
郑商所 | 菜粕 | RM合约月份C行权价格 | RM合约月份P行权价格 | RM109C3000 | RM109P3000 |
郑商所 | 动力煤 | ZC合约月份C行权价格 | ZC合约月份P行权价格 | ZC109C800 | ZC109P800 |
查询期权合约代码的例子:
function main(){
var productsForFind = null
while(true){
if(exchange.IO("status")){
LogStatus(_D(), "已经连接CTP !")
var ret = exchange.IO("instruments")
ret.forEach(function(product) {
// Log(product)
// 这里设置要查的名字,i铁矿石合约
if (product.InstrumentName.indexOf("i") != -1 && (product.InstrumentName.indexOf("P") != -1 || product.InstrumentName.indexOf("C") != -1)) {
Log(product, "#FF0000")
productsForFind = product
}
})
break
} else {
LogStatus(_D(), "未连接CTP !")
}
Sleep(1000)
}
Log(productsForFind, "#FF0000")
}
支持中泰证券(XTP)。
支持富途牛牛实盘交易、模拟盘交易,需要下载FutuOpenD
软件。当使用FutuOpenD
接入模拟交易时有些股票代码不支持所以无法交易,但是富途牛牛手机APP是可以模拟交易的。在优宽量化上配置交易所对象、运行FutuOpenD
软件等操作参看富途证券配置说明文档。
接口调用频率
对于GetOrder
、GetOrders
、GetPosition
、GetAccount
函数默认使用缓存数据,所以不限制调用频率。当有新数据时FutuOpenD
会自动更新数据,缓存数据也会同步更新。
调用exchange.IO("refresh", true)
函数可以禁用缓存,如果禁用缓存则调用频率为每30秒内最多请求10次查询,超过频率限制会报错。
股票代码
例如:600519.SH
策略代码中使用exchange.SetContractType()
函数设置股票代码,例如:
function main() {
var info = exchange.SetContractType("600519.SH") // 设置为股票600519.SH即贵州茅台,账户即切换到大陆市场
Log(info)
Log(exchange.GetAccount()) // 当前设置的股票为贵州茅台,此时调用GetAccount函数获取账户资产,获取的为大陆市场账户资产
Log(exchange.GetTicker()) // 获取股票贵州茅台当前的价格信息
}
def main():
info = exchange.SetContractType("600519.SH")
Log(info)
Log(exchange.GetAccount())
Log(exchange.GetTicker())
void main() {
auto info = exchange.SetContractType("600519.SH");
Log(info);
Log(exchange.GetAccount());
Log(exchange.GetTicker());
}
设置交易方向的函数exchange.SetDirection
、下单函数exchange.Buy
/exchange.Sell
、撤单函数exchange.CancelOrder
、查询订单函数exchange.GetOrder
等使用方法均和商品期货市场相同。
账户信息数据格式:
使用TrdMarket
定义市场,用以区分香港市场
,美国市场
,大陆市场
。
摘录自Futu API
文档:
const (
TrdMarket_TrdMarket_Unknown TrdMarket = 0 //未知市场
TrdMarket_TrdMarket_HK TrdMarket = 1 //香港市场
TrdMarket_TrdMarket_US TrdMarket = 2 //美国市场
TrdMarket_TrdMarket_CN TrdMarket = 3 //大陆市场
TrdMarket_TrdMarket_HKCC TrdMarket = 4 //香港A股通市场
TrdMarket_TrdMarket_Futures TrdMarket = 5 //期货市场
)
获取账户信息数据,exchange.GetAccount()
函数返回:
{
"Info": [{
"Header": {
... // 省略
"TrdMarket": 1 // Info原始信息中,市场ID,表示该账户资产用于香港市场交易
},
"Funds": { // 账户在该市场的资产信息
...
}
}, ...],
"Stocks": 0,
"FrozenStocks": 0,
"Balance": 1000000, // 当前市场的资产数值
"FrozenBalance": 0
}
FutuOpenD
根据登录的IP地址作为地区区分
对于非大陆IP地址登录的账户在获取行情数据时有所限制,具体可以查阅FutuOpenD
(富途)官方文档。
exchange.SetProxy(...)
,切换为代理服务器访问交易所,该函数无返回值(用变量获取,获取的是undefined
)。如果代理设置失败,在调用接口时会返回空值。仅限rest协议。每个交易所对象exchanges[n]
可以设置一个代理,设置代理后访问交易所接口都会通过代理去访问。
以第一个添加的交易所对象exchange
即:exchanges[0]
为例:
exchange.SetProxy("socks5://127.0.0.1:8889")
exchange.SetProxy("socks5://username:password@127.0.0.1:8889")
。username
为用户名,password
为密码。exchange.SetProxy("")
支持设置交易所对象发出请求的IP地址
全局指定
windows
系统的界面版托管者可以直接设置,如图:
其它命令行环境运行的托管者使用-I
参数指定IP地址。
基于exchange指定
function main(){
exchange.SetProxy("ip://10.0.3.15")
exchange.GetTicker() // 发出的请求IP地址为10.0.3.15
}
C++
编写策略和JavaScript
编写策略区别主要为优宽 API接口返回数据的区别。例如exchange.GetTicker()
函数。
JavaScript
exchange.GetTicker()
调用成功时返回一个对象,如果调用失败(交易所服务器问题、网络问题等等)返回null
。
function main() {
// 鉴于测试代码,不使用商品期货策略一般架构,这里仅仅判断exchange.IO("status")函数,判断连接期货公司前置机成功后立即执行测试代码。股票证券无需使用exchange.IO("status")判断连接状态
while(!exchange.IO("status")) {
Sleep(1000)
}
// 设置合约为rb888即螺纹钢主力连续合约,或者设置股票代码
exchange.SetContractType("rb888")
var ticker = exchange.GetTicker()
// 判断exchange.GetTicker函数是否调用失败,返回null
if (ticker){
Log(ticker)
}
}
C++
exchange.GetTicker()
调用成功时返回一个对象,如果调用失败返回的还是一个对象,和正常返回的对象是通过一个属性Valid
来区别的。
void main() {
while(exchange.IO("status") == 0) {
Sleep(1000);
}
exchange.SetContractType("rb888");
auto ticker = exchange.GetTicker();
// 判断exchange.GetTicker()函数是否调用失败,返回的对象中Valid属性是否是false
if (ticker.Valid) {
Log(ticker);
}
}
C++
策略中的main()
函数与标准C11中main()
函数的区别:
C11中的C++
程序入口函数main()
返回值为int
类型,在优宽的C++
策略中,策略的启动函数也是main()
函数。不过这两者并非是同一个函数,只是同名而已。并且优宽的C++
策略中main()
函数的返回值是void
类型。
void main() {
while(exchange.IO("status") == 0) {
Sleep(1000);
}
exchange.SetContractType("rb888");
// 使用Test函数测试
if (!Test("c++")) {
// 抛出异常,让程序停止
Panic("请下载最新版本托管者");
}
// 所有的对像返回用Valid来判断是否有效
LogProfitReset();
LogReset();
Log(_N(9.12345, 2));
Log("use _C", _C(exchange.GetTicker), _C(exchange.GetAccount));
}
从系统底层真正支持JavaScript
语言策略的多线程功能。包括:并发执行自定义的执行函数;支持并发线程之间的通信、支持并发线程与主线程之间的通信;储存、共享线程环境中的变量等功能。目前仅支持实盘环境中使用,参考帖子:https://www.youquant.com/digest-topic/9451。
__Thread(function, arguments...)
函数创建一个并发运行的线程,不支持引用线程执行函数以外的变量(做为隔离环境运行),引用外部变量将会编译失败。也不支持对其它闭包函数引用,线程内部可调用平台所有API,但不能调用用户自定义的其它函数。参数function
可以为函数引用或者匿名函数。参数arguments
为function
函数的参数(实际传入的参数),arguments...
表示可以传多个。返回值:线程Id。
function testFunc(n) {
Log("执行函数testFunc,参数n:", n)
}
function main() {
var testThread1 = __Thread(function () {
Log("执行匿名函数,没有参数。")
})
var testThread2 = __Thread(testFunc, 10) // 参数n : 10
__threadJoin(testThread1) // 可以使用__threadJoin函数等待并发的线程执行完毕
__threadJoin(testThread2) // 如果没有等待testThread1、testThread2执行完成,主线程先执行完毕后会自动释放并发的线程,终止并发的线程的执行函数
}
支持__Thread([function, arguments...], [function, arguments...], ...)
的调用方式,即创建的线程中顺序执行多个线程执行函数。
function threadTestFuncA(a) {
Log(a)
threadTestFuncC(4)
// 可以调用threadTestFuncC函数,但是不能调用threadTestFuncB函数
// this.d
Log(d)
}
function threadTestFuncB(b) {
Log(b)
threadTestFuncC(2)
this.d = 5
}
function main() {
// 先执行 threadTestFuncB 函数,再执行 threadTestFuncA 函数
// threadTestFuncC 不会自动执行,但是可以被其它线程执行函数调用
var threadId = __Thread([threadTestFuncA, 3], [threadTestFuncB, 1], ["function threadTestFuncC(c) {Log(c)}"])
__threadJoin(threadId)
}
传入__Thread
函数的
henryp1 怎么不显示目录?
雨幕(youquant) 哪个目录 ?