商品期货API文档

Author: 雨幕(youquant), Created: 2021-10-20 19:26:38, Updated: 2023-04-06 17:56:09

何访问交易所接口的API函数时(如exchange.GetTicker()exchange.Buy(Price, Amount)exchange.CancelOrder(Id)等)都有可能由于各种原因导致访问失败。所以要对这些函数的调用做容错处理,举例:exchange.GetTicker()获取行情数据函数可能由于交易所服务器问题、网络通信问题等原因导致exchange.GetTicker()函数返回的值为null。这时就要对exchange.GetTicker()返回的值做容错处理。

如下代码exchange.GetTicker()函数返回的数据赋值给ticker变量,我们在使用ticker这个变量之前就需要对其容错处理。

function main() {
    // 鉴于测试代码,不使用商品期货策略一般架构,这里仅仅判断exchange.IO("status")函数,判断连接期货公司前置机成功后立即执行测试代码。股票证券无需使用exchange.IO("status")判断连接状态
    while(!exchange.IO("status")) {
        Sleep(1000)
    }
    Log(exchange.SetContractType("rb888")) 

    var ticker = exchange.GetTicker()
    if(!ticker){
        // 重新调用一次,或者其它处理逻辑
        ticker = exchange.GetTicker()
    }
}
def main():
    while not exchange.IO("status"):
        Sleep(1000)
    Log(exchange.SetContractType("rb888"))

    ticker = exchange.GetTicker()
    if not ticker:
        ticker = exchange.GetTicker()
void main() {
    while(exchange.IO("status") == 0) {
        Sleep(1000);
    }
    Log(exchange.SetContractType("rb888"));

    auto ticker = exchange.GetTicker();
    if(!ticker.Valid) {
        ticker = exchange.GetTicker();
        Log("测试");
    }
}

另外对于策略的容错性能测试,FMZ专门在回测中增加了独有的容错模式回测。回测系统可以根据设置的参数随机给一些实盘时会发生网络访问的API接口返回一些失败调用时的返回值,可以快速的检测程序在实盘中的健壮性。 在策略编辑页面切换到回测系统页面,点击「开始回测」按钮右侧的倒三角下拉控件,会弹出「容错模式回测」按钮。

exchange.GetTicker()

exchange.GetTicker(),获取当前交易对、合约对应的市场当前行情,返回值:Ticker结构体。 回测系统中exchange.GetTicker()函数返回的Ticker数据,其中HighLow为模拟值,取自当时盘口的卖一、买一。

function main(){
    // 鉴于测试代码,不使用商品期货策略一般架构,这里仅仅判断exchange.IO("status")函数,判断连接期货公司前置机成功后立即执行测试代码。股票证券无需使用exchange.IO("status")判断连接状态
    while(!exchange.IO("status")) {
        Sleep(1000)
    }
    Log(exchange.SetContractType("rb888"))

    var ticker = exchange.GetTicker()
    Log("High:", ticker.High, "Low:", ticker.Low, "Sell:", ticker.Sell, "Buy:", ticker.Buy, "Last:", ticker.Last, "Volume:", ticker.Volume)
}
def main():
    while not exchange.IO("status"):
        Sleep(1000)
    Log(exchange.SetContractType("rb888"))

    ticker = exchange.GetTicker()
    Log("High:", ticker["High"], "Low:", ticker["Low"], "Sell:", ticker["Sell"], "Buy:", ticker["Buy"], "Last:", ticker["Last"], "Volume:", ticker["Volume"])
void main() {
    while(exchange.IO("status") == 0) {
        Sleep(1000);
    }
    Log(exchange.SetContractType("rb888"));

    auto ticker = exchange.GetTicker();
    Log("High:", ticker.High, "Low:", ticker.Low, "Sell:", ticker.Sell, "Buy:", ticker.Buy, "Last:", ticker.Last, "Volume:", ticker.Volume);
}

商品期货策略实盘中如果没有行情推送过来时,exchange.GetTicker()函数会阻塞,等待行情推送。 exchange.GetDepth()exchange.GetTrades()exchange.GetRecords()同理。如果不希望阻塞可以使用切换行情模式

  • exchange.IO("mode", 0) 立即返回模式,如果当前还没有接收到交易所最新的行情数据推送,就立即返回旧的行情数据,如果有新的数据就返回新的数据。

  • exchange.IO("mode", 1) 缓存模式(默认模式),如果当前还没有收到交易所最新的行情数据(同上一次接口获取的数据比较),就等待接收然后再返回,如果调用该函数之前收到了最新的行情数据,就立即返回最新的数据。

  • exchange.IO("mode", 2) 强制更新模式,进入等待一直到接收到交易所下一次的最新推送数据后返回。

实盘时(非回测)exchange.GetTicker()函数的返回值中Info属性储存接口调用时返回的原始数据。

  • 商品期货 返回商品期货CTP协议/易盛协议接口应答数据。

    CTP协议为例:

    {
        "BidPrice4": 1.7976931348623157e+308,
        "AskVolume4": 0,
        "AskVolume5": 0,
        "Turnover": 26229625880,
        "OpenInterest": 1364847,                  // 持仓量
        "ClosePrice": 1.7976931348623157e+308,
        "LowerLimitPrice": 3473,
        "BidPrice3": 1.7976931348623157e+308,
        "ExchangeID": "",
        "BidPrice2": 1.7976931348623157e+308,
        "BidPrice5": 1.7976931348623157e+308,
        "AveragePrice": 37323.89130239898,
        "BidVolume4": 0,
        "BidVolume5": 0,
        "ExchangeInstID": "",
        "LowestPrice": 3715,
        "Volume": 702757,
        "BidVolume3": 0,
        "AskPrice3": 1.7976931348623157e+308,
        "AskVolume3": 0,
        "ActionDay": "20200714",
        "PreClosePrice": 3739,
        "SettlementPrice": 1.7976931348623157e+308,
        "UpdateTime": "13:40:01",
        "BidPrice1": 3727,
        "AskPrice2": 1.7976931348623157e+308,
        "UpperLimitPrice": 3996,
        "CurrDelta": 1.7976931348623157e+308,
        "UpdateMillisec": 500,
        "AskVolume1": 154,
        "BidVolume2": 0,
        "PreOpenInterest": 1372843,
        "PreDelta": 0,
        "AskPrice1": 3728,
        "AskVolume2": 0,
        "TradingDay": "20200714",
        "InstrumentID": "rb2010",
        "LastPrice": 3727,
        "HighestPrice": 3749,
        "BidVolume1": 444,
        "PreSettlementPrice": 3735,
        "OpenPrice": 3740,
        "AskPrice4": 1.7976931348623157e+308,
        "AskPrice5": 1.7976931348623157e+308
    }
    

exchange.GetDepth()

exchange.GetDepth(),获取当前交易对、合约对应的市场的订单薄数据,返回值:Depth结构体。

Depth结构体包含两个结构体数组,分别是Asks[]Bids[]AsksBids包含以下结构体变量:

数据类型 变量名 说明
number Price 价格
number Amount 数量

例如我想获取当前卖一价,可以这么写代码:

function main(){
    // 鉴于测试代码,不使用商品期货策略一般架构,这里仅仅判断exchange.IO("status")函数,判断连接期货公司前置机成功后立即执行测试代码。股票证券无需使用exchange.IO("status")判断连接状态
    while(!exchange.IO("status")) {
        Sleep(1000)
    }
    Log(exchange.SetContractType("rb888"))

    var depth = exchange.GetDepth()
    var price = depth.Asks[0].Price
    Log("卖一价为:", price)
}
def main():
    while not exchange.IO("status"):
        Sleep(1000)
    Log(exchange.SetContractType("rb888"))

    depth = exchange.GetDepth()
    price = depth["Asks"][0]["Price"]
    Log("卖一价为:", price)
void main() {
    while(exchange.IO("status") == 0) {
        Sleep(1000);
    }
    Log(exchange.SetContractType("rb888"));

    auto depth = exchange.GetDepth();
    auto price = depth.Asks[0].Price;
    Log("卖一价为:", price);
}

商品期货实盘时需要注意: 涨停时卖单卖一的价格是涨停价格,订单量是0。跌停时买单买一的价格是跌停价格,订单量是0。通过判断买一卖一订单数据中的订单量,可以判断出是否涨跌停。

exchange.GetTrades()

商品期货、股票证券不支持该函数。商品期货可以通过算法根据行情数据推算出市场成交记录。

策略例子:

exchange.GetRecords()

exchange.GetRecords(Period),返回K线数据。K线周期在创建实盘时指定,如果在调用exchange.GetRecords()函数时指定了参数,获取的就是该参数周期对应的K线数据。如果函数调用时没有指定参数,按照实盘参数上设置的K线周期或者回测页面设置的K线周期返回对应的K线数据。

参数Period

  • PERIOD_M1:指1分钟
  • PERIOD_M5:指5分钟
  • PERIOD_M15:指15分钟
  • PERIOD_M30:指30分钟
  • PERIOD_H1:指1小时
  • PERIOD_D1:指一天

参数Period的值除了可以传以上定义的标准周期,还可以传入数值,单位为秒。K线周期设置1分钟15分钟1天时为直接获取K线数据,其它周期则使用次一级别周期的K线数据合成。

exchange.GetRecords(Period)函数的返回值: 返回值为Record结构体数组,返回的K线数据会随时间累积,累积K线柱数量的上限受到exchange.SetMaxBarLen函数设置的影响,没有设置过时默认上限为5000个K线柱。当K线数据到达K线柱累积上限,之后会更新加入一根K线柱的同时删除一根最早时间的K线柱(如队列进出)。

初始调用GetRecords函数时获取的K线柱数量:

  • 回测系统中会预先取回测周期起始时刻前1000根K线柱,作为初始K线数据。

对于exchange.GetRecords(Period)函数来说商品期货的实盘、回测均支持自定义周期,参数Period为秒数,例如:

function main() {
    // 鉴于测试代码,不使用商品期货策略一般架构,这里仅仅判断exchange.IO("status")函数,判断连接期货公司前置机成功后立即执行测试代码。股票证券无需使用exchange.IO("status")判断连接状态
    while(!exchange.IO("status")) {
        Sleep(1000)
    }
    Log(exchange.SetContractType("rb888"))

    // 打印K线周期为120秒(2分钟)的K线数据
    Log(exchange.GetRecords(60 * 2))
    // 打印K线周期为5分钟的K线数据
    Log(exchange.GetRecords(PERIOD_M5))      
}
def main():
    while not exchange.IO("status"):
        Sleep(1000)
    Log(exchange.SetContractType("rb888"))

    Log(exchange.GetRecords(60 * 2))
    Log(exchange.GetRecords(PERIOD_M5))
void main() {
    while(exchange.IO("status") == 0) {
        Sleep(1000);
    }
    Log(exchange.SetContractType("rb888"));

    Log(exchange.GetRecords(60 * 2)[0]);
    Log(exchange.GetRecords(PERIOD_M5)[0]);
}

Period参数设置为5,即为请求获取5秒为周期的K线数据。 如果Period参数不能被60整除(即代表的周期是不可用分钟为单位的周期),系统底层则使用tick数据合成所需的K线数据。 如果Period参数能被60整除,则最小使用1分钟K线数据(尽可能使用较大的周期合成所需K线数据),合成所需的K线数据。

function main(){
    // 鉴于测试代码,不使用商品期货策略一般架构,这里仅仅判断exchange.IO("status")函数,判断连接期货公司前置机成功后立即执行测试代码。股票证券无需使用exchange.IO("status")判断连接状态
    while(!exchange.IO("status")) {
        Sleep(1000)
    }
    Log(exchange.SetContractType("rb888"))

    var records = exchange.GetRecords(PERIOD_H1)
    Log("第一根k线数据为,Time:", records[0].Time, "Open:", records[0].Open, "High:", records[0].High)
    Log("第二根k线数据为,Time:", records[1].Time ,"Close:", records[1].Close)
    Log("当前K线(最新)", records[records.length-1], "上一根K线", records[records.length-2])
}
def main():
    while not exchange.IO("status"):
        Sleep(1000)
    Log(exchange.SetContractType("rb888"))

    records = exchange.GetRecords(PERIOD_H1)
    Log("第一根k线数据为,Time:", records[0]["Time"], "Open:", records[0]["Open"], "High:", records[0]["High"])
    Log("第二根k线数据为,Time:", records[1]["Time"], "Close:", records[1]["Close"])
    Log("当前K线(最新)", records[-1], "上一根K线", records[-2])
void main() {
    while(exchange.IO("status") == 0) {
        Sleep(1000);
    }
    Log(exchange.SetContractType("rb888"));

    auto records = exchange.GetRecords(PERIOD_H1);
    Log("第一根k线数据为,Time:", records[0].Time, "Open:", records[0].Open, "High:", records[0].High);
    Log("第二根k线数据为,Time:", records[1].Time, "Close:", records[1].Close);
    Log("当前K线(最新)", records[records.size() - 1], "上一根K线", records[records.size() - 2]);
}

股票证券:

  • 富途证券 在K线周期为日线周期以下时,调用GetRecords()函数返回的数据中数组的每个元素为一个K线柱数据(即Record结构体),每个K线柱数据结构的Time属性是该周期的结束时间(毫秒级时间戳)并非起始时间(毫秒级时间戳)。 在K线周期为日线周期时,Record结构体Time属性是该周期的起始时间(毫秒级时间戳)。

注意: 回测系统中模拟级别回测由于需要设置底层K线周期(回测系统模拟级别回测时,根据设置的底层K线周期使用对应的K线数据生成tick数据),需要注意在策略中获取的K线数据的周期不能小于底层K线周期。因为在模拟级别回测中,各个周期的K线数据在回测系统中都是通过底层K线周期对应的K线数据合成的。

C++语言中如果需要自己构造K线数据有以下代码范例:

#include <sstream>
void main() { 
    Records r;
    r.Valid = true;
    for (auto i = 0; i < 10; i++) {
        Record ele;
        ele.Time = i * 100000;
        ele.High = i * 10000;
        ele.Low = i * 1000;
        ele.Close = i * 100;
        ele.Open = i * 10;
        ele.Volume = i * 1;
        r.push_back(ele);
    }
    // 输出显示:Records[10]
    Log(r);                      
    auto ma = TA.MA(r,10);       
    // 输出显示:[nan,nan,nan,nan,nan,nan,nan,nan,nan,450]
    Log(ma);                     
}

exchange.GetPeriod()

exchange.GetPeriod()函数返回回测实盘运行策略时在发明者量化交易平台网站页面上设置的K线周期,返回值为整数,单位为秒。返回值:数值类型。

function main() {
    // 例如回测、实盘时发明者量化交易平台网站页面上设置的K线周期为1小时
    var period = exchange.GetPeriod()
    Log("K线周期:", period / (60 * 60), "小时")
}
def main():
    period = exchange.GetPeriod()
    Log("K线周期:", period / (60 * 60), "小时")
void main() {
    auto period = exchange.GetPeriod();
    Log("K线周期:", period / (60 * 60.0), "小时");
}

exchange.SetMaxBarLen(Len)

exchange.SetMaxBarLen(Len)函数对于商品期货策略来说,运行时影响K线线柱(BAR)的上限数量。exchange.SetMaxBarLen函数需要在exchange.SetContractType函数调用之前执行,因为当exchange.SetContractType函数调用时,系统底层已经开始处理K线数据。

function main() {
    // 鉴于测试代码,不使用商品期货策略一般架构,这里仅仅判断exchange.IO("status")函数,判断连接期货公司前置机成功后立即执行测试代码。股票证券无需使用exchange.IO("status")判断连接状态
    while(!exchange.IO("status")) {
        Sleep(1000)
    }
    exchange.SetMaxBarLen(50)
    Log(exchange.SetContractType("rb888"))
    
    var records = exchange.GetRecords()
    Log(records.length, records)
}
def main():
    while not exchange.IO("status"):
        Sleep(1000)
    exchange.SetMaxBarLen(50)
    Log(exchange.SetContractType("rb888"))
    
    r = exchange.GetRecords()
    Log(len(r), r)
void main() {
    while(exchange.IO("status") == 0) {
        Sleep(1000);
    }
    exchange.SetMaxBarLen(50);
    Log(exchange.SetContractType("rb888"));
    
    auto r = exchange.GetRecords();
    Log(r.size(), r[0]);
}

exchange.GetRawJSON()

exchange.GetRawJSON(),返回最后一次请求时获取的原始内容(字符串),可以用来自行解析数据。返回值:字符串类型。 C++语言策略不支持该函数。

function main(){
    while(!exchange.IO("status")) {
        Sleep(1000)
    }

    exchange.GetAccount(); 
    var obj = JSON.parse(exchange.GetRawJSON());
    Log(obj);
}
import json
def main():
    while not exchange.IO("status"):
        Sleep(1000)

    exchange.GetAccount()
    obj = json.loads(exchange.GetRawJSON())
    Log(obj)
void main() {
    auto obj = exchange.GetAccount();
    // C++ 不支持GetRawJSON函数
    Log(obj);
}

回测系统中商品期货策略只有JavaScript语言策略支持。

function main(){
    while(true){
        if(exchange.IO("status")){
            exchange.SetContractType("rb888")
            var ticker = exchange.GetTicker()
            exchange.SetDirection("buy")
            exchange.Buy(ticker.Sell + 2, 1)            
            Log("exchange.GetAccount()函数返回的数据中的Balance:", exchange.GetAccount().Balance)
            var ret = JSON.parse(exchange.GetRawJSON())
            Log("exchange.GetRawJSON()函数返回的数据中的Balance:", ret.Balance)
            break
            LogStatus(_D(), "已经连接CTP !")
        } else {
            LogStatus(_D(), "未连接CTP !")
        }
    }
}

exchange.GetRate()

exchange.GetRate(),返回交易所使用的流通货币与当前显示的计价货币的汇率,返回1表示禁用汇率转换。返回值:数值类型。

注意:

  • 如果没有调用exchange.SetRate()设置过转换汇率,exchange.GetRate()默认返回的汇率值是1,即当前显示的计价货币没有发生过汇率转换。
  • 如果使用exchange.SetRate()设置过一个汇率值,例如exchange.SetRate(7),那么当前exchange这个交易所对象代表的交易所流通货币的行情、深度、下单价格等等所有价格信息,都会被乘以之前设置的汇率7进行转换。
  • 如果exchange对应的是以美元为计价货币的交易所,调用exchange.SetRate(7)后,实盘所有价格都会被乘7转换成接近CNY的价格。此时使用exchange.GetRate()获取的汇率值就是7。

exchange.GetUSDCNY()

exchange.GetUSDCNY(),返回美元最新汇率(yahoo提供的数据源)。返回值:数值类型。

exchange.SetData(Key, Value)

exchange.SetData(Key, Value)函数用于设置策略运行时加载的数据,可以是任何经济指标、行业数据、相关指数等。用于策略量化考核所有可量化的信息,支持回测系统中使用。

exchange.SetData(Key, Value)函数的调用方式:

  • 在策略中直接写入数据 要求数据格式如同以下例子中的data变量。

    /*backtest
    start: 2020-01-21 00:00:00
    end: 2020-02-12 00:00:00
    period: 1d
    basePeriod: 1d
    exchanges: [{"eid":"Futures_CTP","currency":"FUTURES"}]
    */
    function main() {
        var data = [
            [1579536000000, "abc"],
            [1579622400000, 123],
            [1579708800000, {"price": 123}],
            [1579795200000, ["abc", 123, {"price": 123}]]
        ]
        exchange.SetData("test", data)
        while(true) {
            Log(exchange.GetData("test"))
            Sleep(1000)
        }
    }
    
    '''backtest
    start: 2020-01-21 00:00:00
    end: 2020-02-12 00:00:00
    period: 1d
    basePeriod: 1d
    exchanges: [{"eid":"Futures_CTP","currency":"FUTURES"}]
    '''  
    
    def main():
        data = [
            [1579536000000, "abc"],
            [1579622400000, 123],
            [1579708800000, {"price": 123}],
            [1579795200000, ["abc", 123, {"price": 123}]]
        ]
        exchange.SetData("test", data)
        while True:
            Log(exchange.GetData("test"))
            Sleep(1000)
    
    /*backtest
    start: 2020-01-21 00:00:00
    end: 2020-02-12 00:00:00
    period: 1d
    basePeriod: 1d
    exchanges: [{"eid":"Futures_CTP","currency":"FUTURES"}]
    */  
    
    void main() {
        json data = R"([
            [1579536000000, "abc"],
            [1579622400000, 123],
            [1579708800000, {"price": 123}],
            [1579795200000, ["abc", 123, {"price": 123}]]
        ])"_json;
        
        exchange.SetData("test", data);
        while(true) {
            Log(exchange.GetData("test"));
            Sleep(1000);
        }
    }
    

    以上测试代码运行时,会在对应的时间获取对应的数据,如图:

    img

    可以看到时间戳1579622400000对应的时间为2020-01-22 00:00:00,当策略程序运行在这个时间之后,在下一条数据时间戳1579708800000即时间2020-01-23 00:00:00之前,调用exchange.GetData(Source)函数获取数据。获取的都是[1579622400000, 123]该条数据的内容,随着程序继续运行,时间变化,以此类推获取逐条数据。

  • 通过外部链接请求数据

    请求到的数据格式

    {
        "schema":["time","data"],
        "data":[
            [1579536000000, "abc"],
            [1579622400000, 123],
            [1579708800000, {"price": 123}],
            [1579795200000, ["abc", 123, {"price": 123}]]
        ]
    }
    

    其中schema为加载数据的主体中的每一条记录的数据格式,该格式固定为["time","data"]对应data属性中的逐条数据的格式。data属性中储存的为数据主体,每条数据由毫秒级别时间戳和数据内容构成(数据内容可以是任何可JSON编码的数据)。

    测试用的服务程序,使用Go语言编写:

    package main
    import (
        "fmt"
        "net/http"
        "encoding/json"
    )  
    
    func Handle (w http.ResponseWriter, r *http.Request) {
        defer func() {
            fmt.Println("req:", *r)
            ret := map[string]interface{}{
                "schema": []string{"time","data"},
                "data": []interface{}{
                    []interface{}{1579536000000, "abc"},
                    []interface{}{1579622400000, 123},
                    []interface{}{1579708800000, map[string]interface{}{"price":123}},
                    []interface{}{1579795200000, []interface{}{"abc", 123, map[string]interface{}{"price":123}}},
                },
            }
            b, _ := json.Marshal(ret)
            w.Write(b)
        }()
    }  
    
    func main () {
        fmt.Println("listen http://localhost:9090")
        http.HandleFunc("/data", Handle)
        http.ListenAndServe(":9090", nil)
    }
    

    程序在接收到请求后的应答数据:

    {
        "schema":["time","data"],
        "data":[
            [1579536000000, "abc"],
            [1579622400000, 123],
            [1579708800000, {"price": 123}],
            [1579795200000, ["abc", 123, {"price": 123}]]
        ]
    }
    

    测试策略代码:

    /*backtest
    start: 2020-01-21 00:00:00
    end: 2020-02-12 00:00:00
    period: 1d
    basePeriod: 1d
    exchanges: [{"eid":"Futures_CTP","currency":"FUTURES"}]
    */
    function main() {
        while(true) {
            Log(exchange.GetData("http://xxx.xx.x.xx:9090/data"))
            Sleep(1000)
        }
    }
    
    '''backtest
    start: 2020-01-21 00:00:00
    end: 2020-02-12 00:00:00
    period: 1d
    basePeriod: 1d
    exchanges: [{"eid":"Futures_CTP","currency":"FUTURES"}]
    '''  
    
    def main():
        while True:
            Log(exchange.GetData("http://xxx.xx.x.xx:9090/data"))
            Sleep(1000)
    
    /*backtest
    start: 2020-01-21 00:00:00
    end: 2020-02-12 00:00:00
    period: 1d
    basePeriod: 1d
    exchanges: [{"eid":"Futures_CTP","currency":"FUTURES"}]
    */  
    
    void main() {
        while(true) {
            Log(exchange.GetData("http://xxx.xx.x.xx:9090/data"));
            Sleep(1000);
        }
    }
    

exchange.GetData(Source)

exchange.GetData(Source)函数用于获取exchange.SetData(Key, Value)函数加载的数据或外部链接提供的数据,支持回测系统中使用。回测时一次性获取数据,实盘时缓存一分钟的数据。

  • 获取直接写入的数据的调用方式

    /*backtest
    start: 2020-01-21 00:00:00
    end: 2020-02-12 00:00:00
    period: 1d
    basePeriod: 1d
    exchanges: [{"eid":"Futures_CTP","currency":"FUTURES"}]
    */
    function main() {
        exchange.SetData("test", [[1579536000000, _D(1579536000000)], [1579622400000, _D(1579622400000)], [1579708800000, _D(1579708800000)]])
        while(true) {
            Log(exchange.GetData("test"))
            Sleep(1000 * 60 * 60 * 24)
        }
    }
    
    '''backtest
    start: 2020-01-21 00:00:00
    end: 2020-02-12 00:00:00
    period: 1d
    basePeriod: 1d
    exchanges: [{"eid":"Futures_CTP","currency":"FUTURES"}]
    '''  
    def main():
        exchange.SetData("test", [[1579536000000, _D(1579536000000/1000)], [1579622400000, _D(1579622400000/1000)], [1579708800000, _D(1579708800000/1000)]])
        while True:
            Log(exchange.GetData("test"))
            Sleep(1000 * 60 * 60 * 24)
    
    /*backtest
    start: 2020-01-21 00:00:00
    end: 2020-02-12 00:00:00
    period: 1d
    basePeriod: 1d
    exchanges: [{"eid":"Futures_CTP","currency":"FUTURES"}]
    */    
    void main() {
        json arr = R"([[1579536000000, ""], [1579622400000, ""], [1579708800000, ""]])"_json;
        arr[0][1] = _D(1579536000000);
        arr[1][1] = _D(1579622400000);
        arr[2][1] = _D(1579708800000);
        exchange.SetData("test", arr);
        while(true) {
            Log(exchange.GetData("test"));
            Sleep(1000 * 60 * 60 * 24);
        }
    }
    
  • 获取外部链接的数据的调用方式

    function main() {
        Log(exchange.GetData("http://xxx.xx.x.xx:9090/data"))
        Log(exchange.GetData("https://www.fmz.com/upload/asset/32bf73a69fc12d36e76.json"))
    }
    
    def main():
        Log(exchange.GetData("http://xxx.xx.x.xx:9090/data"))
        Log(exchange.GetData("https://www.fmz.com/upload/asset/32bf73a69fc12d36e76.json"))
    
    void main() {
        Log(exchange.GetData("http://xxx.xx.x.xx:9090/data"));
        Log(exchange.GetData("https://www.fmz.com/upload/asset/32bf73a69fc12d36e76.json"));
    }
    
  • 使用平台数据中心的基本面数据 使用exchange.GetData(Source)函数获取基本面数据

在调用exchange.GetData(Source)函数时,可以传入第二个参数,用于设置缓存超时,单位为毫秒。实盘时默认为一分钟缓存超时,回测系统中,当使用访问接口请求数据的方式时,回测系统会自动给请求增加from(时间戳秒),to(时间戳秒),period(底层K线周期,时间戳毫秒)等参数,用于确定要获取数据的时间范围。

交易API

以下函数均可通过exchangeexchanges[0]对象调用。例如:exchange.Sell(100, 1);,即在交易所下一个订单价格为100,数量为1的卖单。

exchange.Buy(Price, Amount)

exchange.Buy(Price, Amount),该函数用于下买单,该函数返回一个订单ID。参数值:Price为订单价格,数值类型。Amount为订单数量,数值类型。exchange.Buy(Price, Amount)函数的返回值:字符串类型。 返回的订单编号,可用于查询订单信息和取消订单。

期货下单时必须注意交易方向是否设置正确,如果交易方向和交易函数不匹配会报错。参看:exchange.SetDirection

function main() {
    // 鉴于测试代码,不使用商品期货策略一般架构,这里仅仅判断exchange.IO("status")函数,判断连接期货公司前置机成功后立即执行测试代码。股票证券无需使用exchange.IO("status")判断连接状态
    while (!exchange.IO("status")) {
        Sleep(1000)
    }
    // 设置合约代码
    exchange.SetContractType("rb888")
    // 设置下单方向
    exchange.SetDirection("buy")

    // 注意,这里的下单价格100为举例,具体测试的时候可以自行设置、改动
    var id = exchange.Buy(100, 1)
    Log("id:", id)
}
def main():
    while not exchange.IO("status"):
        Sleep(1000)
    exchange.SetContractType("rb888")
    exchange.SetDirection("buy")

    id = exchange.Buy(100, 1)
    Log("id:", id)
void main() {
    while (exchange.IO("status") == 0) {
        Sleep(1000);
    }
    exchange.SetContractType("rb888");
    exchange.SetDirection("buy");

    auto id = exchange.Buy(100, 1);
    Log("id:", id);
}
  • 股票证券下单

    下单量为股票股数,并非股票手数(在股票证券交易所对象中如无特殊说明,相关的量均为股票股数)。下单量需要符合股票信息中的每手股数要求。 例如,02333.HK港股长城汽车,每手500股。下单量必须是500的整倍数。601633.SH为A股长城汽车,每手100股。下单量必须是100的整倍数。 每手股数可以从SetContractType函数返回的数据结构中获取(VolumeMultiple字段)。也可以从GetTicker()函数返回的数据结构中的Info属性中获取(LotSize字段)。

  • 市价单

    价格参数传-1即可。回测系统不支持商品期货、股票证券市价单,实盘支持。

    function main() {
        // 鉴于测试代码,不使用商品期货策略一般架构,这里仅仅判断exchange.IO("status")函数,判断连接期货公司前置机成功后立即执行测试代码。股票证券无需使用exchange.IO("status")判断连接状态
        while (!exchange.IO("status")) {
            Sleep(1000)
        }
        // 设置合约代码
        exchange.SetContractType("rb888")
        // 设置下单方向
        exchange.SetDirection("buy")  
    
        // 下单价格参数传-1即为市价单
        var id = exchange.Buy(-1, 1)
        Log("id:", id)
    }
    
    def main():
        while not exchange.IO("status"):
            Sleep(1000)
        exchange.SetContractType("rb888")
        exchange.SetDirection("buy")  
    
        id = exchange.Buy(-1, 1)
        Log("id:", id)
    
    void main() {
        while (exchange.IO("status") == 0) {
            Sleep(1000);
        }
        exchange.SetContractType("rb888");
        exchange.SetDirection("buy");
    
        auto id = exchange.Buy(-1, 1);
        Log("id:", id);
    }
    

商品期货、股票证券除了使用市价单(价格参数传-1,实际是系统使用了一个较高的买入价或者较低的卖出价来确保成交),还可以用限价单方式下单。可以使用一个较大的滑价,确保和对手盘成交,参看exchange.Sell中的范例。

exchange.Sell(Price, Amount)

exchange.Sell(Price, Amount),该函数用于下卖单,该函数返回一个订单ID。参数值:Price为订单价格,数值类型。Amount为订单数量,数值类型。exchange.Sell(Price, Amount)函数的返回值:字符串类型。 返回的订单编号,可用于查询订单信息和取消订单。

期货下单时必须注意交易方向是否设置正确,如果交易方向和交易函数不匹配会报错。参看:exchange.SetDirection

function main(){
    // 鉴于测试代码,不使用商品期货策略一般架构,这里仅仅判断exchange.IO("status")函数,判断连接期货公司前置机成功后立即执行测试代码。股票证券无需使用exchange.IO("status")判断连接状态
    while (!exchange.IO("status")) {
        Sleep(1000)
    }
    // 设置合约代码
    exchange.SetContractType("rb888")
    // 设置下单方向
    exchange.SetDirection("sell")

    // 注意,这里的下单价格100为举例,具体测试的时候可以自行设置、改动
    var id = exchange.Sell(100, 1)
    Log("id:", id)
}
def main():
    while not exchange.IO("status"):
        Sleep(1000)
    exchange.SetContractType("rb888")
    exchange.SetDirection("sell")

    id = exchange.Sell(100, 1)
    Log("id:", id)
void main() {
    while (exchange.IO("status") == 0) {
        Sleep(1000);
    }
    exchange.SetContractType("rb888");
    exchange.SetDirection("sell");

    auto id = exchange.Sell(100, 1);
    Log("id:", id);
}
  • 股票证券下单

    下单量为股票股数,并非股票手数(在股票证券交易所对象中如无特殊说明,相关的量均为股票股数)。下单量需要符合股票信息中的每手股数要求。参看

  • 市价单

    价格参数传-1即可。回测系统不支持商品期货、股票证券市价单,实盘支持。

    参看

商品期货除了使用市价单还可以用限价单方式下单,可以使用一个较大的滑价确保和对手盘成交。

function main() {
    while(true) {
        if (exchange.IO("status")) {
            // 设置当前合约为 rb2001 , 测试的时候按回测时间设置合适的合约
            exchange.SetContractType("rb2001")               
            exchange.SetDirection("buy")
            // 获取当前行情
            var ticker = exchange.GetTicker()                
            // 拿到当前卖一价格
            var currSell1Price = ticker.Sell                 
            // 加50元滑价,即为比出价卖出的挂单高50,要求买入1手
            var id = exchange.Buy(currSell1Price + 50, 1)    
            Log(exchange.GetOrder(id))
            break
        } else {
            Log("未连接")
        }
    }
}
def main():
    while True:
        if exchange.IO("status"):
            exchange.SetContractType("rb2001")
            exchange.SetDirection("buy")
            ticker = exchange.GetTicker()
            currSell1Price = ticker["Sell"]
            id = exchange.Buy(currSell1Price + 50, 1)
            Log(exchange.GetOrder(id))
            break
        else :
            Log("未连接")
void main() {
    while(true) {
        if(exchange.IO("status") == 1) {
            exchange.SetContractType("rb2001");
            exchange.SetDirection("buy");
            auto ticker = exchange.GetTicker();
            auto currSell1Price = ticker.Sell;
            auto id = exchange.Buy(currSell1Price + 50, 1);
            Log(exchange.GetOrder(id));
            break;
        } else {
            Log("未连接");
        }
    }
}

exchange.CancelOrder(Id)

exchange.CancelOrder(Id),该函数的用途为取消某个Id的订单。参数值:Id为订单编号,参数Id的类型为字符串类型。返回值:布尔类型。

返回操作结果,true表示取消订单请求发送成功,false表示取消订单请求发送失败(返回值只是代表发送请求成功或失败,交易所是否取消订单可以调用exchange.GetOrders()判断)。

function main(){
    // 鉴于测试代码,不使用商品期货策略一般架构,这里仅仅判断exchange.IO("status")函数,判断连接期货公司前置机成功后立即执行测试代码。股票证券无需使用exchange.IO("status")判断连接状态
    while (!exchange.IO("status")) {
        Sleep(1000)
    }
    // 设置合约代码
    exchange.SetContractType("rb888")
    // 设置下单方向
    exchange.SetDirection("sell")

    // 下单价格只是举例,较大的价格不会成交,订单会处于订单薄中待成交状态,具体测试可以自行调整价格
    var id = exchange.Sell(99999, 1)
    exchange.CancelOrder(id)
}
def main():
    while not exchange.IO("status"):
        Sleep(1000)
    exchange.SetContractType("rb888")
    exchange.SetDirection("sell")

    id = exchange.Sell(99999, 1)
    exchange.CancelOrder(id)
void main() {
    while (exchange.IO("status") == 0) {
        Sleep(1000);
    }
    exchange.SetContractType("rb888");
    exchange.SetDirection("sell");

    auto id = exchange.Sell(99999, 1);
    exchange.CancelOrder(id);
}

FMZ的API函数中可以产生日志输出的函数例如:Log(...)exchange.Buy(Price, Amount)exchange.CancelOrder(Id)等都可以在必要参数后跟一些附带输出参数。 例如:exchange.CancelOrder(orders[j].Id, orders[j]),这样就是在取消Id为orders[j].Id的这个订单时附带输出这个订单的信息,即orders[j]这个Order结构。

function main() {
    // 鉴于测试代码,不使用商品期货策略一般架构,这里仅仅判断exchange.IO("status")函数,判断连接期货公司前置机成功后立即执行测试代码。股票证券无需使用exchange.IO("status")判断连接状态
    while (!exchange.IO("status")) {
        Sleep(1000)
    }
    // 设置合约代码
    exchange.SetContractType("rb888")
    // 设置下单方向
    exchange.SetDirection("sell")    

    Log("数据1", "数据2", "数据3", "...")
    var data2 = 200
    // 下单价格只是举例,较大的价格不会成交,订单会处于订单薄中待成交状态,具体测试可以自行调整价格
    var id = exchange.Sell(100000, 0.1, "附带数据1", data2, "...")
    exchange.CancelOrder(id, "附带数据1", data2, "...")
    LogProfit(100, "附带数据1", data2, "...")
}
def main():
    while not exchange.IO("status"):
        Sleep(1000)
    exchange.SetContractType("rb888")
    exchange.SetDirection("sell")

    Log("数据1", "数据2", "数据3", "...")
    data2 = 200
    id = exchange.Sell(100000, 0.1, "附带数据1", data2, "...")
    exchange.CancelOrder(id, "附带数据1", data2, "...")
    LogProfit(100, "附带数据1", data2, "...")
void main() {
    while (exchange.IO("status") == 0) {
        Sleep(1000);
    }
    exchange.SetContractType("rb888");
    exchange.SetDirection("sell");

    Log("数据1", "数据2", "数据3", "...");
    int data2 = 200;
    auto id = exchange.Sell(100000, 0.1, "附带数据1", data2, "...");
    exchange.CancelOrder(id, "附带数据1", data2, "...");
    LogProfit(100, "附带数据1", data2, "...");
}

exchange.GetOrder(Id)

exchange.GetOrder(Id),根据订单号获取订单详情。参数值:Id为需要获取的订单号,参数Id为字符串类型。返回值:Order结构体。

function main(){
    // 鉴于测试代码,不使用商品期货策略一般架构,这里仅仅判断exchange.IO("status")函数,判断连接期货公司前置机成功后立即执行测试代码。股票证券无需使用exchange.IO("status")判断连接状态
    while (!exchange.IO("status")) {
        Sleep(1000)
    }
    // 设置合约代码
    exchange.SetContractType("rb888")
    // 设置下单方向
    exchange.SetDirection("sell")

    // 下单价格只是举例,较大的价格不会成交,订单会处于订单薄中待成交状态,具体测试可以自行调整价格
    var id = exchange.Sell(99999, 1)
    // 参数id为订单号码,需填入你想要查询的订单的号码
    var order = exchange.GetOrder(id)      
    Log("Id:", order.Id, "Price:", order.Price, "Amount:", order.Amount, "DealAmount:",
        order.DealAmount, "Status:", order.Status, "Type:", order.Type)
}
def main():
    while not exchange.IO("status"):
        Sleep(1000)
    exchange.SetContractType("rb888")
    exchange.SetDirection("sell")

    id = exchange.Sell(99999, 1)
    order = exchange.GetOrder(id)
    Log("Id:", order["Id"], "Price:", order["Price"], "Amount:", order["Amount"], "DealAmount:", 
        order["DealAmount"], "Status:", order["Status"], "Type:", order["Type"])
void main() {
    while (exchange.IO("status") == 0) {
        Sleep(1000);
    }
    exchange.SetContractType("rb888");
    exchange.SetDirection("sell");

    auto id = exchange.Sell(99999, 1);
    auto order = exchange.GetOrder(id);
    Log("Id:", order.Id, "Price:", order.Price, "Amount:", order.Amount, "DealAmount:", 
        order.DealAmount, "Status:", order.Status, "Type:", order.Type);
}

exchange.GetOrders()

exchange.GetOrders(),获取所有未完成的订单。返回值:Order结构体数组。 Order结构体可参考exchange.GetOrder()函数说明。当交易所对象exchange代表的账户当前交易对没有挂单时,调用exchange.GetOrders()返回空数组,即:[]

function main(){
    // 鉴于测试代码,不使用商品期货策略一般架构,这里仅仅判断exchange.IO("status")函数,判断连接期货公司前置机成功后立即执行测试代码。股票证券无需使用exchange.IO("status")判断连接状态
    while (!exchange.IO("status")) {
        Sleep(1000)
    }
    // 设置合约代码
    exchange.SetContractType("rb888")
    // 设置下单方向
    exchange.SetDirection("sell")

    // 下单价格只是举例,较大的价格不会成交,订单会处于订单薄中待成交状态,具体测试可以自行调整价格
    exchange.Sell(99999, 1)
    exchange.Sell(88888, 1)
    var orders = exchange.GetOrders()
    Log("未完成订单一的信息,ID:", orders[0].Id, "Price:", orders[0].Price, "Amount:", orders[0].Amount,
        "DealAmount:", orders[0].DealAmount, "type:", orders[0].Type)
    Log("未完成订单二的信息,ID:", orders[1].Id, "Price:", orders[1].Price, "Amount:", orders[1].Amount,
        "DealAmount:", orders[1].DealAmount, "type:", orders[1].Type)
}
def main():
    while not exchange.IO("status"):
        Sleep(1000)
    exchange.SetContractType("rb888")
    exchange.SetDirection("sell")

    exchange.Sell(99999, 1)
    exchange.Sell(88888, 1)
    orders = exchange.GetOrders()
    Log("未完成订单一的信息,ID:", orders[0]["Id"], "Price:", orders[0]["Price"], "Amount:", orders[0]["Amount"], 
        "DealAmount:", orders[0]["DealAmount"], "type:", orders[0]["Type"])
    Log("未完成订单二的信息,ID:", orders[1]["Id"], "Price:", orders[1]["Price"], "Amount:", orders[1]["Amount"],
        "DealAmount:", orders[1]["DealAmount"], "type:", orders[1]["Type"])
void main() {
    while (exchange.IO("status") == 0) {
        Sleep(1000);
    }
    exchange.SetContractType("rb888");
    exchange.SetDirection("sell");

    exchange.Sell(99999, 1);
    exchange.Sell(88888, 1);
    auto orders = exchange.GetOrders();
    Log("未完成订单一的信息,ID:", orders[0].Id, "Price:", orders[0].Price, "Amount:", orders[0].Amount, 
        "DealAmount:", orders[0].DealAmount, "type:", orders[0].Type);
    Log("未完成订单二的信息,ID:", orders[1].Id, "Price:", orders[1].Price, "Amount:", orders[1].Amount,
        "DealAmount:", orders[1].DealAmount, "type:", orders[1].Type);
}

exchange.GetOrders()函数在商品期货、股票证券中获取的是所有未完成订单。在商品期货中exchange.GetOrders()函数获取的订单与当前设置的合约无关。可以使用以下例子进行回测、模拟盘、实盘测试。

/*backtest
start: 2020-06-17 10:00:00
end: 2020-06-18 00:00:00
period: 1d
basePeriod: 1h
exchanges: [{"eid":"Futures_CTP","currency":"FUTURES"}]
*/

function main() {
    var contractTypeList = ["MA009", "rb2010", "i2009"]
    while (true) {
        if (exchange.IO("status")) {
            for (var i = 0; i < contractTypeList.length; i++) {
                var ret = exchange.SetContractType(contractTypeList[i])
                var ticker = exchange.GetTicker()
                exchange.SetDirection("sell")
                var id = exchange.Sell(ticker.Sell + 5, 1)
                Log(contractTypeList[i], "开空仓订单ID:", id)
            }
            var orders = exchange.GetOrders()
            for (var j = 0; j < orders.length; j++) {
                Log(orders[j])
            }
            break
        } else {
            LogStatus(_D(), "未连接")
        }
    }
}
'''backtest
start: 2020-06-17 10:00:00
end: 2020-06-18 00:00:00
period: 1d
basePeriod: 1h
exchanges: [{"eid":"Futures_CTP","currency":"FUTURES"}]
'''

def main():
    contractTypeList = ["MA009", "rb2010", "i2009"]
    while True:
        if exchange.IO("status"):
            for i in range(len(contractTypeList)):
                ret = exchange.SetContractType(contractTypeList[i])
                ticker = exchange.GetTicker()
                exchange.SetDirection("sell")
                id = exchange.Sell(ticker["Sell"] + 5, 1)
                Log(contractTypeList[i], "开空仓订单ID:", id)
            
            orders = exchange.GetOrders()
            for i in range(len(orders)):
                Log(orders[i])                            
            break
        else:
            LogStatus(_D(), "未连接")
/*backtest
start: 2020-06-17 10:00:00
end: 2020-06-18 00:00:00
period: 1d
basePeriod: 1h
exchanges: [{"eid":"Futures_CTP","currency":"FUTURES"}]
*/

void main() {
    vector<string> contractTypeList = {"MA009", "rb2010", "i2009"};
    while (true) {
        if (exchange.IO("status") == 1) {
            for (int i = 0 ; i < contractTypeList.size(); i++) {
                auto ret = exchange.SetContractType(contractTypeList[i]);
                auto ticker = exchange.GetTicker();
                exchange.SetDirection("sell");
                auto id = exchange.Sell(ticker.Sell + 5.0, 1);
                Log(contractTypeList[i], "开空仓订单ID:", id);
            }
            auto orders = exchange.GetOrders();
            for (int j = 0; j < orders.size(); j++) {
                Log(orders[j]);
            }
            break;
        } else {
            LogStatus(_D(), "未完成");
        }
    } 
}

exchange.SetPrecision(…)

exchange.SetPrecision(PricePrecision, AmountPrecision),设置价格与品种下单量的小数位精度,设置后会自动截断数据多余的小数位。参数值:PricePrecision为数值类型,用来控制价格数据的小数点位数。AmountPrecision为数值类型,用来控制下单量数据的小数点位数。PricePrecisionAmountPrecision都必须是整型的数值。

function main(){
    // 设置价格小数位精度为2位,品种下单量小数位精度为3位
    exchange.SetPrecision(2, 3)
}    
def main():
    exchange.SetPrecision(2, 3)
void main() {
    exchange.SetPrecision(2, 3);
}

注意: 回测不支持该函数,回测的数值精度会自动处理。

exchange.SetRate(Rate)

exchange.SetRate(Rate),设置交易所的流通货币的汇率。参数值:Rate为数值类型。返回值:为数值类型。

function main(){
    // 鉴于测试代码,不使用商品期货策略一般架构,这里仅仅判断exchange.IO("status")函数,判断连接期货公司前置机成功后立即执行测试代码。股票证券无需使用exchange.IO("status")判断连接状态
    while (!exchange.IO("status")) {
        Sleep(1000)
    }
    // 设置合约代码
    exchange.SetContractType("rb888")

    // 设置汇率之前打印行情数据
    Log(exchange.GetTicker())
    // 设置汇率转换
    exchange.SetRate(7)
    Log(exchange.GetTicker())
    // 设置为1,不转换
    exchange.SetRate(1)
    Log(exchange.GetTicker())
}
def main():
    while not exchange.IO("status"):
        Sleep(1000)
    exchange.SetContractType("rb888")

    Log(exchange.GetTicker())
    exchange.SetRate(7)
    Log(exchange.GetTicker())
    exchange.SetRate(1)
    Log(exchange.GetTicker())
void main() {
    while (exchange.IO("status") == 0) {
        Sleep(1000);
    }
    exchange.SetContractType("rb888");

    Log(exchange.GetTicker());
    exchange.SetRate(7);
    Log(exchange.GetTicker());
    exchange.SetRate(1);
    Log(exchange.GetTicker());
}

注意:

  • 如果使用exchange.SetRate(Rate)设置过一个汇率值,例如设置为7。那么当前exchange这个交易所对象代表的交易所的流通货币的行情、深度、下单价格等等所有价格信息,都会被乘以设置的汇率7进行转换。
  • 例如exchange是以美元为计价货币的交易所。exchange.SetRate(7)调用后,实盘所有价格都会被乘7转换成接近CNY的价格。

exchange.IO(…)

exchange.IO(...),调用协议、交易所等其它功能接口,参数有多种模式。

使用mode参数,切换行情模式:

  • exchange.IO("mode", 0) 立即返回模式,如果当前还没有接收到交易所最新的行情数据推送,就立即返回旧的行情数据,如果有新的数据就返回新的数据。 商品期货中的应用,可以参考:exchange.IO函数的wait参数的使用例子。

  • exchange.IO("mode", 1) 缓存模式(默认模式),如果当前还没有收到交易所最新的行情数据(同上一次接口获取的数据比较),就等待接收然后再返回,如果调用该函数之前收到了最新的行情数据,就立即返回最新的数据。

  • exchange.IO("mode", 2) 强制更新模式,进入等待一直到接收到交易所下一次的最新推送数据后返回。

使用status参数,判断与期货公司前置机连接状态:

exchange.IO("status"),返回true证明与CTP服务器行情与数据的两台服务器都连接正常。

function main() {
    while (!exchange.IO("status")) {
        LogStatus("正在等待与交易服务器连接, " + new Date())
    }
}
def main():
    while not exchange.IO("status"):
        LogStatus("正在等待与交易服务器连接, " + _D())
void main() {
    while(exchange.IO("status") == 0) {
        LogStatus("正在等待与交易服务器连接, " + _D());
    }
}

使用wait参数,设置阻塞:

exchange.IO("wait", Timeout),当前交易所有任何品种更新行情信息或订单成交时才返回,可带第二个参数(毫秒数)指定超时,超时返回空值,正常返回EventTick/OrderEvent结构,结合exchange.IO("mode", 0)函数使用,这样


更多内容

henryp1 怎么不显示目录?

雨幕(youquant) 哪个目录 ?