資源の読み込みに... 荷物...

発明者 量化取引プラットフォーム 汎用プロトコル 独自の取引所へのアクセス

作者: リン・ハーン発明者 量化 - 微かな夢作成日:2017年8月24日 16:29:56 更新日:2021年6月10日 15:33:02

汎用プロトコルの使用文書

この通用プロトコルのいずれかにアクセスできます.API取引の取引所,特定のAPIプロトコルの制限はありません.restwebsocketfixメディアは,インターネットの利用者を対象としています.

Pythonの一般的なプロトコルの例:https://www.fmz.com/strategy/101399

  • ### 1 汎用プロトコルのプラグインを実行 ポート設定

盗聴アドレスとポート設定を書き込み 通用プロトコルプラグイン 例えば:http://127.0.0.1:6666/DigitalAssetメディアは,http://127.0.0.1:6666/exchange

なぜこれを設定するのか?IP経路ほら ほら 原因は発明者の量化ページコントロールセンタージャンプエクスチェンジを追加ページをクリックすると, "General Protocol" を選択します.API-KEYサービスアドレスのアドレスは,ホストがどのIPとポートにアクセスすべきかを伝える (ホストとUPRは同じデバイスで実行できない) サービスアドレスの例を記入します.http://127.0.0.1:6666/DigitalAssetDigitalAssetこの名前は自明な名前ですが,例として挙げます.

発明者による量化追加取引所のページは,以下のように見えます. 通常,取引所では,アカウントの設定情報を設定するだけです.access keyそしてsecret keyしかし,一部の取引所のAPIインターフェイスは取引パスワードを伝達することを要求する (例えば,一部の取引所の下記のインターフェイス),このとき,通用プロトコルページに余分なコントロールが書き込まれるので,そのような取引所のAPIに出くわしたとき,余分な伝達が必要な設定情報を書き込むことができます.secret keyメールの送信は,メールの送信先に表示されます.access key文字列を書き換えるには,split図示のように,このデータを分離する操作をします.

发明者量化交易平台通用协议接入自定义交易所

プラグインで処理し,それを取得します.XXX_PassWord│ │ この記事の最後の完全な例では,newBitgoの関数は,

  func newBitgo(accessKey, secretKey string) *iBitgo {
      s := new(iBitgo)
      s.accessKey = accessKey
      s.secretKey = secretKey
      // 在此可以分离secretKey中的额外配置信息,可以写成如下注释中的内容
      /*
      arr := strings.SplitN(secretKey, ",", 2)
      if len(arr) != 2 {
          panic("配置错误!")
      }
      s.secretKey = arr[0]   // secret key 
      s.passWord = arr[1]    // XXX_PassWord
      */
      s.apiBase = "https://www.bitgo.cn"
      s.timeout = 20 * time.Second
      s.timeLocation = time.FixedZone("Asia/Shanghai", 8*60*60)

      return s
  }

发明者量化交易平台通用协议接入自定义交易所

汎用プロトコルプラグインmain函数の例:


  ```go
  func main(){
      var addr = flag.String("b", "127.0.0.1:6666", "bing addr")   // 设置命令行参数,默认值描述,端口设置6666
      flag.Parse()                                                 // 解析命令行
      if *addr == "" {
          flag.Usage()                                             // 显示命令行描述
          return 
      }
      basePath := "/DigitalAsset"
      log.Println("Running ", fmt.Sprintf("http://%s%s", *addr, basePath), "...")   // 打印监听端口信息
      http.HandleFunc(basePath, OnPost)
      http.ListenAndServe(*addr, nil)
  }
  • この関数で,

一般プロトコルのプラグインを操作する パックプログラムは,指定されたポートを絶えず監視し,要求が送信されているか確認します.request■リクエストがあったら,応答関数を呼び出し,リクエストデータのパラメータを解析し,ホストが送信するリクエストデータは:

  /* request的JSON结构,发明者量化调用GetTicker,托管者发送给通用协议插件情况举例(各个API调用时,params的值可能不一样,此处method为ticker):
  {
      "access_key" : "XXX",               // `json:"access_key"`
      "secret_key" : "XXX",               // `json:"secret_key"`
      "nonce" : "1502345965295519602",    // `json:"nonce"`
      "method" : "ticker",                // `json:"method"`
      "params" : {                        // `json:"params"`
                     "symbol" : "btc",
                     ...
                 },                       // 各个请求参数略有区别。即在策略中调用不同的 发明者量化 API会有不同的参数, 在下文各个API 有介绍说明。
  }
  */

JSONのデータに対する反シリアル化によって得られる構造です.request中身Methodコンピュータはswitch異なる発明者による量化処理を分類するAPI (つまり,ホスト上で実行されるポリシーを識別する呼び出しがどの発明者による量化であるかを識別する)API呼び出し:

  ```go
  switch request.Method {    // 此处request.Method的M为大写,通用协议程序接收到的请求主体中为JSON数据,在Go语言中反JSON序列化(Unmarshal)为结构体,字段首字母必须大写
    case "accounts" :        // 当托管者上的机器人策略中调用了exchange.GetAccount()函数,托管者会发送来请求,其中Body携带的数据中method属性值为accounts
        data, err = e.GetAccount(symbol)
    case "ticker" :
        data, err = e.GetTicker(symbol)
    case "depth" :
        data, err = e.GetDepth(symbol)
    case "trades" :
        data, err = e.GetTrades(symbol)
    case "trade" :
    ...
    default:
        ...

これらのブランチは,実行後に返されるデータを,通用プロトコルプラグインが返答する構造に書き込み,托管者の要求に応答する.

Go言語の例:

  defer func(){                                // 处理收尾工作 
        if e := recover(); e != nil {          // recover()函数用于捕获panic,e != nil即有错误发生
            if ee, ok := e.(error); ok {       // 类型推导,推导成功把ee.Error()赋值给e
                e = ee.Error()                 // 调用Error方法获取返回的错误字符串
            }
            ret = map[string]string{"error": fmt.Sprintf("%v", e)}
        }
        b, _ := json.Marshal(ret)              // 把本次调用获取的结果ret编码,赋值给b,写入响应指针
        w.Write(b)
        //fmt.Println("after w.Write the b is", string(b))     // 测试
    }()
  • #### 3 APIの呼び出しの種類

基本的には2つのカテゴリーに分かれます. 署名を必要としない公共インターフェース,例えば:

  
  ```GetDepth()```

  ```GetTrades()```

  ```GetRecords(period)```
  ...

  2、 需要签名的 用户接口, 如:

  ```Buy```、```Sell```

  ```GetOrder(id)```

  ```GetOrders()```

  ```GetAccount()```

  ```CancelOrder(id)```
  ...
  各个交易所的签名方式可能各不相同,需要根据需求具体编写。

- ### 4、发明者量化各个API接口调用时**通用协议插件**和**托管者**交互的数据格式:
  
  > 一些发明者量化的API接口如```GetName()```,```GetLabel()```等函数,调用时不会向**通用协议插件**发送请求。
    ```exchange.GetName()```通用插件配置的交易所调用时会返回"Exchange"。
  
  - #### 1、GetTicker: **用于获取当前行情数据。**
  
    **托管者**发送给监听响应函数的```request```中的```method```为:```ticker```。
    
    托管者发送参数:```request.Params.symbol```:由托管者根据机器人页面设置的币种发送。
    
    **托管者向通用协议插件请求时请求主体携带的数据格式(JSON)**
    
    ```
    {
        "access_key" : "access_key",
        "secret_key" : "secret_key",
        "nonce" :      "1500793319499",            // 毫秒时间戳
        "method" :     "ticker",
        "params" :     {"symbol" : "ETH_BTC"},     // 以ETH_BTC交易对举例
    }
    ```

    **最终发送给托管者的返回值结构:(即通用协议插件请求交易所接口后的数据返回给托管者的格式)**

    JSON结构
    ```
    {
        "data": {
            "time": 1500793319499,  // 毫秒时间戳,整型
            "buy": 1000,            // 以下浮点型
            "sell": 1001,
            "last": 1005,
            "high": 1100,
            "low": 980,
            "vol": 523,
        }
    }
    ```

  - #### 2、GetRecords:**用于获取交易所提供的K线数据。(根据托管者请求的参数获取)**
  
    托管者发送给监听响应函数的```request```中的```method```为:```records```

    托管者发送参数:```request.Params.period```:值关联```exchange.GetRecords```函数的第一个参数,实际```request.Params.period```为分钟数表示的周期,例如日周期为```60*24```即```1440```、```request.Params.symbol```:托管者根据设置的币种发送。
    
    **托管者向通用协议插件请求时请求主体携带的数据格式**
    
    ```
    {
        "access_key" : "access_key",
        "secret_key" : "secret_key",
        "nonce" :      "1500793319499",            // 毫秒时间戳
        "method" :     "records",
        "params" :     {"symbol" : "ETH_BTC", "period" : "1440"},     // 以ETH_BTC交易对,K线周期为日线举例
    }
    ```

    **最终发送给托管者的 返回值结构:**

    JSON结构

    ```
    {
        "data": [
                [1500793319, 1.1, 2.2, 3.3, 4.4, 5.5],         // "Time":1500793319000,"Open":1.1,"High":2.2,"Low":3.3,"Close":4.4,"Volume":5.5
                [1500793259, 1.01, 2.02, 3.03, 4.04, 5.05],
                ...
        ]
    }
    ```

    Go语言测试数据:
    ```
    ret_records = []interface{}{
        [6]interface{}{1500793319, 1.1, 2.2, 3.3, 4.4, 5.5}, 
        [6]interface{}{1500793259, 1.01, 2.02, 3.03, 4.04, 5.05}
    }
    ```
    
    发明者量化平台```Log```显示```records```数据:
    ```
    [
        {"Time":1500793319000,"Open":1.1,"High":2.2,"Low":3.3,"Close":4.4,"Volume":5.5},
        {"Time":1500793259000,"Open":1.01,"High":2.02,"Low":3.03,"Close":4.04,"Volume":5.05}
    ]
    ```
    注意:1、第二维数组中的第一个元素是```int```类型,代表时间戳。2、托管者会自动给时间戳乘1000,以上可以见。

  - #### 3、GetDepth:**获取交易所的深度信息(订单薄,卖一、卖二...买一、买二...)**
    
    托管者发送给监听响应函数的```request```中的```method```为:```depth```

    托管者发送参数:```request.Params.symbol```:由托管者根据策略设置的币种发送
    
    **托管者向通用协议插件请求时请求主体携带的数据格式**
    ```
    {
        "access_key" : "access_key",
        "secret_key" : "secret_key",
        "nonce" :      "1500793319499",            // 毫秒时间戳
        "method" :     "depth",
        "params" :     {"symbol" : "ETH_BTC"},     // 以ETH_BTC交易对举例
    }
    ```
    
    **最终发送给托管者的返回值结构:**

    JSON结构
    ```
    {
        "data" : {
            "time" : 1500793319499,
            "asks" : [ [1000, 0.5], [1001, 0.23], [1004, 2.1], ... ],
            "bids" : [ [999, 0.25], [998, 0.8], [995, 1.4], ... ],
        }
    }
    ```

  - #### 4、GetTrades:**获取交易所提供的整个交易所一定时间内的交易记录(非自己的交易记录)**
  
    托管者发送给监听响应函数的```request```中的```method```为:```trades```

    托管者发送参数:```request.Params.symbol```值为交易币种例如:```btc```,由托管者根据策略设置的币种发送。
    
    **托管者向通用协议插件请求时请求主体携带的数据格式**
    ```
    {
        "access_key" : "access_key",
        "secret_key" : "secret_key",
        "nonce" :      "1500793319499",            // 毫秒时间戳
        "method" :     "trades",
        "params" :     {"symbol" : "ETH_BTC"},     // 以ETH_BTC交易对举例
    }
    ```
    
    **最终发送给托管者的返回值结构:**

    JSON结构
    ```
    { 
        "data": [
            {
                "id": 12232153,
                "time" : 1529919412968,
                "price": 1000,
                "amount": 0.5,
                "type": "buy",             // "buy"、"sell"
            },{
                "id": 12545664,
                "time" : 1529919412900,
                "price": 1001,
                "amount": 1,
                "type": "sell",
            },{
                ...
            }
        ]
    }
    ```

  - #### 5、GetAccount:**获取账户资产信息**
  
    托管者发送给监听响应函数的```request```中的```method```为:```accounts```

    托管者发送参数:(需注意!一般是获取账户的所有资产!,具体看交易所接口,是单独获取还是获取总资产信息)
    
    **托管者向通用协议插件请求时请求主体携带的数据格式**
    ```
    {
        "access_key" : "access_key",
        "secret_key" : "secret_key",
        "nonce" :      "1500793319499",            // 毫秒时间戳
        "method" :     "accounts",
        "params" :     {},                         
    }
    ```
    
    **最终发送给托管者的 返回值结构:**

    JSON结构
    ```
    {
        "data": [
            {"currency": "btc", "free": 1.2, "frozen": 0.1},
            {"currency": "ltc", "free": 25, "frozen": 2.1},
            {"currency": "ltc", "free": 25, "frozen": 2.1},
            ...
        ],
        "raw" : {...}             // 可以写入插件访问交易所时,交易所返回的原始信息(response)
    }
    ```

  - #### 6、Buy、Sell:**发送委托单,下单交易。(市价单、限价单)**
  
    托管者发送给监听响应函数的```request```中的```method```为:```trade```
    
    托管者发送参数:```request.Params.type```:托管者根据调用```exchange.Buy```还是```exchange.Sell```发送、```request.Params.price```:策略中调用的```API```函数的第一个参数、```request.Params.amount```:策略中调用的```API```函数的第二个参数、```request.Params.symbol```:托管者根据设置的币种发送。
    
    **托管者向通用协议插件请求时请求主体携带的数据格式**
    ```
    {
        "access_key" : "access_key",
        "secret_key" : "secret_key",
        "nonce" :      "1500793319499",            // 毫秒时间戳
        "method" :     "trade",
        "params" :     {
                           "symbol" : "ETH_BTC", 
                           "type" : "buy", 
                           "price" : "1000", 
                           "amount" : "1"
                       },                          // 以ETH_BTC交易对,"type":"buy"买请求,价格1000,数量1举例
    }
    ```
    
    **最终发送给托管者的返回值结构:**

    JSON结构
    ```
    {
        "data": {
            "id": 125456,      // 下单后返回的订单id
                               // 如果订单id是"asdf346sfasf532"这样的字符串形式
                               // 此处id也可以是字符串类型
        }
    }
    ```

  - #### 7、GetOrder:**获取指定订单号的订单信息**
  
    托管者发送给监听响应函数的```request```中的```method```为:```order```
    
    托管者发送参数:```request.Params.id```、```request.Params.symbol```
    
    **托管者向通用协议插件请求时请求主体携带的数据格式**
    ```
    {
        "access_key" : "access_key",
        "secret_key" : "secret_key",
        "nonce" :      "1500793319499",            // 毫秒时间戳
        "method" :     "order",
        "params" :     {"symbol" : "ETH_BTC", "id" : "XXX"},     // 以ETH_BTC交易对,订单id为XXX举例,注意有些交易所的订单号是数字形式的订单ID,如123456,有些交易所的订单号是字符串形式的ID,如poimd55sdfheqxv,具体看交易所的订单ID格式
    }
    ```
    
    **最终发送给托管者的返回值结构:**

    JSON结构

    ```
    { 
        "data": {
            "id": 2565244,
            "amount": 0.15,
            "price": 1002,
            "status": "open",    // "open":挂起状态、"closed":完成关闭状态、"cancelled":已取消
            "deal_amount": 0,
            "type": "buy",       // "buy"、"sell"
            "avg_price": 0,      // 如果交易所没有提供,在处理时可以赋值为0
        }
    }
    ```

  - #### 8、GetOrders: **获取所有未完成的订单信息**
  
    托管者发送给监听响应函数的```request```中的method为:```orders```

    托管者发送参数:```request.Params.symbol```
    
    **托管者向通用协议插件请求时请求主体携带的数据格式**
    ```
    {
        "access_key" : "access_key",
        "secret_key" : "secret_key",
        "nonce" :      "1500793319499",            // 毫秒时间戳
        "method" :     "orders",
        "params" :     {"symbol" : "ETH_BTC"},     // 以ETH_BTC交易对举例
    }
    ```
    
    **最终发送给托管者的返回值结构:**

    JSON结构
    ```
    {
        "data": [{
            "id": 542156,
            "amount": 0.25,
            "price": 1005,
            "deal_amount": 0,
            "type": "buy",      // "buy"、"sell"
            "status": "open",   // "open"
        },{
            ...
        }]
    }
    ```

  - #### 9、CancelOrder: **取消指定订单号的订单委托**
  
    托管者发送给监听响应函数的```request```中的```method```为:```cancel```

    托管者发送参数:```request.Params.id```:字符串类型,策略调用的API函数的第一个参数、```request.Params.symbol```:btc(举例)由托管者根据策略设置的币种发送

    **托管者向通用协议插件请求时请求主体携带的数据格式**
    ```
    {
        "access_key" : "access_key",
        "secret_key" : "secret_key",
        "nonce" :      "1500793319499",            // 毫秒时间戳
        "method" :     "cancel",
        "params" :     {"symbol" : "ETH_BTC", "id" : "XXX"},     // 以ETH_BTC交易对,id为"XXX"(同GetOrder函数的参数id一样),举例
    }
    ```

    **最终发送给托管者的返回值结构:**

    JSON结构
    ```
    {
        "data": true,        // true or false
    }
    ```
    
  - #### 10、IO: **调用发明者量化平台的exchange.IO函数**
    
    托管者发送给监听响应函数的```request```中的```method```为以```__api_```开头的的方法名。
    
    **托管者向通用协议插件请求时请求主体携带的数据格式**
    ```
    {
        "access_key" : "access_key",
        "secret_key" : "secret_key",
        "nonce" :      "1500793319499",            // 毫秒时间戳
        "method" :     "__api_XXX",                // XXX为具体交易所的API接口(不包含基地址)
        "params" :     {"borrow_id" : "123", "symbol" : "cny"},      // 具体是传入IO函数的参数
    }
    ```
    
    **最终发送给托管者的返回值结构:**
    
    ```
    {
        "data": {...}       // 具体的接口调用的返回值
    }
    ```
    
    举例,策略调用:
    ```
    var io_str = exchange.IO("api", "POST", "cancel_borrow", "symbol=cny&borrow_id=123")
    ```
          
    插件中的测试代码(go语言):
    ```go
    fmt.Println("request.Method:", request.Method, "request.Params:", request.Params)
    ```
          
    插件命令行 :
    2017/08/31 10:19:59 Running  http://127.0.0.1:6666/DigitalAsset ...
    
    **插件命令行中打印出的request.Method,request.Params**
    托管者发送的请求Body中数据解析后的request中:```request.Method```为:```__api_cancel_borrow```
    托管者发送的请求Body中数据解析后的request中:```request.Params```为:```{"borrow_id" : "123", "symbol" : "cny"}```
    
    可自定义处理这些直接访问交易所```API```的```exchange.IO```调用。
  
    ```
    # 注意:
    # 在调用exchange.IO("api", "POST", "/api/v1/getAccount", "symbol=BTC_USDT")时,
    # 如果第二个参数不是POST,而是:exchange.IO("api", "GET", "/api/v1/getAccount", "symbol=BTC_USDT")
    # 是GET方法,这时在通用协议插件接受到的http请求中头部Http-Method中储存的才是GET,
    # 所以在通用协议处理IO函数实现时,需要参考以下范例代码:
    // tapiCall函数定义
    func (p *iStocksExchange) tapiCall(method string, params map[string]string, httpType string) (js *Json, err error) {
        ...
    }
    
    // 在OnPost函数中
    if strings.HasPrefix(request.Method, "__api_") {
        var js *Json
        js, err = e.tapiCall(request.Method[6:], request.Params, r.Header.Get("Http-Method"))
        ...
    }
    ```
  
  - ### 对于exchange.GetRawJSON的支持
    
    底层自动处理```exchange.GetRawJSON```的调用,不用在插件中实现。
  
  - ### 对于exchange.Go的支持
  
    底层自动处理```exchange.Go```的调用,不用在插件中处理。
    ```
    var beginTime = new Date().getTime()
    var ret = exchange.Go("GetDepth")
    var endTime = new Date().getTime()
    Log(endTime - beginTime, "#FF0000")
    
    // Sleep(2000)
    beginTime = new Date().getTime()
    Log(exchange.GetTicker())
    endTime = new Date().getTime()
    Log(endTime - beginTime, "#FF0000")
    var depth = ret.wait()
    Log("depth:", depth)
    ```
    
    ![发明者量化交易平台通用协议接入自定义交易所](/upload/asset/382d17e0e740b83a822cb5df596e3a1117008039.png) 
    
    ![发明者量化交易平台通用协议接入自定义交易所](/upload/asset/f8253969663d821ebeb6c8111c5ac33b866750dd.png) 
    
    ```
    # 注意:使用exchange.Go在wait的时候如果指定了超时时间, 
    #      一定要确保获取到最终的数据,这样申请的并发线程才能回收。
    ```
  
  - ### 对于期货函数的支持
    需要在插件程序中实现具体处理,例如设置杠杆、合约代码、下单方向,可以设置一个本地变量记录,获取持仓需要访问交易所接口获取原始数据并处理为FMZ平台上定义的持仓结构并返回。
    当策略中调用以下函数时,插件程序接收到的```Rpc```请求格式与其它接口略有差异,需要注意通用协议插件程序中```RpcRequest```的格式,主要区别为params的值是一个复合结构。
    
    - SetContractType      
      设置合约代码。
      ```
      {"access_key":"123","method":"io","nonce":1623307269528738000,"params":{"args":["quarter"],"code":2},"secret_key":"123"}
      ```
  
    - SetDirection
      设置期货下单方向。
      ```
      {"access_key":"123","method":"io","nonce":1623308734966484000,"params":{"args":["closesell"],"code":1},"secret_key":"123"}
      ```

    - SetMarginLevel
      设置期货杠杆。
      ```
      {"access_key":"123","method":"io","nonce":1623308734966939000,"params":{"args":[12],"code":0},"secret_key":"123"}
      ```

    - GetPosition
      获取期货持仓。
      当```exchange.GetPosition()```调用时:
      ```
      {"access_key":"123","method":"io","nonce":1623308734967442000,"params":{"args":[],"code":3},"secret_key":"123"}
      ```
      当```exchange.GetPosition("swap")```调用时:
      ```
      {"access_key":"123","method":"io","nonce":1623308734967442000,"params":{"args":["swap"],"code":3},"secret_key":"123"}
      ```

-------------------------------------------------------------------------------  
    
- ### 通用协议插件完整的Go语言范例(接入比特购交易所)

```go
/*
GOOS=linux GOARCH=amd64 go build -ldflags '-s -w -extldflags -static' rest_bitgo.go
*/
package main

import (
    "bytes"
    "crypto/md5"
    "encoding/hex"
    "encoding/json"
    "errors"
    "flag"
    "fmt"
    "io/ioutil"
    "log"
    "net/http"
    "net/url"
    "sort"
    "strconv"
    "strings"
    "time"
)

func toFloat(s interface{}) float64 {
    var ret float64
    switch v := s.(type) {
    case float64:
        ret = v
    case float32:
        ret = float64(v)
    case int64:
        ret = float64(v)
    case int:
        ret = float64(v)
    case int32:
        ret = float64(v)
    case string:
        ret, _ = strconv.ParseFloat(strings.TrimSpace(v), 64)
    }
    return ret
}

func float2str(i float64) string {
    return strconv.FormatFloat(i, 'f', -1, 64)
}

func toInt64(s interface{}) int64 {
    var ret int64
    switch v := s.(type) {
    case int:
        ret = int64(v)
    case float64:
        ret = int64(v)
    case bool:
        if v {
            ret = 1
        } else {
            ret = 0
        }
    case int64:
        ret = v
    case string:
        ret, _ = strconv.ParseInt(strings.TrimSpace(v), 10, 64)
    }
    return ret
}

func toString(s interface{}) string {
    var ret string
    switch v := s.(type) {
    case string:
        ret = v
    case int64:
        ret = strconv.FormatInt(v, 10)
    case float64:
        ret = strconv.FormatFloat(v, 'f', -1, 64)
    case bool:
        ret = strconv.FormatBool(v)
    default:
        ret = fmt.Sprintf("%v", s)
    }
    return ret
}

type Json struct {
    data interface{}
}

func NewJson(body []byte) (*Json, error) {
    j := new(Json)
    err := j.UnmarshalJSON(body)
    if err != nil {
        return nil, err
    }
    return j, nil
}

func (j *Json) UnmarshalJSON(p []byte) error {
    return json.Unmarshal(p, &j.data)
}

func (j *Json) Get(key string) *Json {
    m, err := j.Map()
    if err == nil {
        if val, ok := m[key]; ok {
            return &Json{val}
        }
    }
    return &Json{nil}
}

func (j *Json) CheckGet(key string) (*Json, bool) {
    m, err := j.Map()
    if err == nil {
        if val, ok := m[key]; ok {
            return &Json{val}, true
        }
    }
    return nil, false
}

func (j *Json) Map() (map[string]interface{}, error) {
    if m, ok := (j.data).(map[string]interface{}); ok {
        return m, nil
    }
    return nil, errors.New("type assertion to map[string]interface{} failed")
}

func (j *Json) Array() ([]interface{}, error) {
    if a, ok := (j.data).([]interface{}); ok {
        return a, nil
    }
    return nil, errors.New("type assertion to []interface{} failed")
}

func (j *Json) Bool() (bool, error) {
    if s, ok := (j.data).(bool); ok {
        return s, nil
    }
    return false, errors.New("type assertion to bool failed")
}

func (j *Json) String() (string, error) {
    if s, ok := (j.data).(string); ok {
        return s, nil
    }
    return "", errors.New("type assertion to string failed")
}

func (j *Json) Bytes() ([]byte, error) {
    if s, ok := (j.data).(string); ok {
        return []byte(s), nil
    }
    return nil, errors.New("type assertion to []byte failed")
}

func (j *Json) Int() (int, error) {
    if f, ok := (j.data).(float64); ok {
        return int(f), nil
    }

    return -1, errors.New("type assertion to float64 failed")
}

func (j *Json) MustArray(args ...[]interface{}) []interface{} {
    var def []interface{}

    switch len(args) {
    case 0:
    case 1:
        def = args[0]
    default:
        log.Panicf("MustArray() received too many arguments %d", len(args))
    }

    a, err := j.Array()
    if err == nil {
        return a
    }

    return def
}

func (j *Json) MustMap(args ...map[string]interface{}) map[string]interface{} {
    var def map[string]interface{}

    switch len(args) {
    case 0:
    case 1:
        def = args[0]
    default:
        log.Panicf("MustMap() received too many arguments %d", len(args))
    }

    a, err := j.Map()
    if err == nil {
        return a
    }

    return def
}

func (j *Json) MustString(args ...string) string {
    var def string

    switch len(args) {
    case 0:
    case 1:
        def = args[0]
    default:
        log.Panicf("MustString() received too many arguments %d", len(args))
    }

    s, err := j.String()
    if err == nil {
        return s
    }

    return def
}

func (j *Json) MustInt64() int64 {
    var ret int64
    var err error
    switch v := j.data.(type) {
    case int:
        ret = int64(v)
    case int64:
        ret = v
    case float64:
        ret = int64(v)
    case string:
        if ret, err = strconv.ParseInt(v, 10, 64); err != nil {
            panic(err)
        }
    default:
        ret = 0
        //panic("type assertion to int64 failed")
    }
    return ret
}

func (j *Json) MustFloat64() float64 {
    var ret float64
    var err error
    switch v := j.data.(type) {
    case int:
        ret = float64(v)
    case int64:
        ret = float64(v)
    case float64:
        ret = v
    case string:
        v = strings.Replace(v, ",", "", -1)
        if ret, err = strconv.ParseFloat(v, 64); err != nil {
            panic(err)
        }
    default:
        ret = 0
        //panic("type assertion to float64 failed")
    }
    return ret
}

type iBitgo struct {
    accessKey     string
    secretKey     string
    currency      string
    opCurrency    string
    baseCurrency  string
    secret        string
    secretExpires int64
    apiBase       string
    step          int64
    newRate       float64
    timeout       time.Duration
    timeLocation  *time.Location
}

type MapSorter []Item

type Item struct {
    Key string
    Val string
}

func NewMapSorter(m map[string]string) MapSorter {
    ms := make(MapSorter, 0, len(m))

    for k, v := range m {
        if strings.HasPrefix(k, "!") {
            k = strings.Replace(k, "!", "", -1)
        }
        ms = append(ms, Item{k, v})
    }

    return ms
}

func (ms MapSorter) Len() int {
    return len(ms)
}

func (ms MapSorter) Less(i, j int) bool {
    //return ms[i].Val < ms[j].Val // 按值排序
    return ms[i].Key < ms[j].Key // 按键排序
}

func (ms MapSorter) Swap(i, j int) {
    ms[i], ms[j] = ms[j], ms[i]
}

func encodeParams(params map[string]string, escape bool) string {
    ms := NewMapSorter(params)
    sort.Sort(ms)

    v := url.Values{}
    for _, item := range ms {
        v.Add(item.Key, item.Val)
    }
    if escape {
        return v.Encode()
    }
    var buf bytes.Buffer
    keys := make([]string, 0, len(v))
    for k := range v {
        keys = append(keys, k)
    }
    sort.Strings(keys)
    for _, k := range keys {
        vs := v[k]
        prefix := k + "="
        for _, v := range vs {
            if buf.Len() > 0 {
                buf.WriteByte('&')
            }
            buf.WriteString(prefix)
            buf.WriteString(v)
        }
    }
    return buf.String()
}

func newBitgo(accessKey, secretKey string) *iBitgo {
    s := new(iBitgo)
    s.accessKey = accessKey
    s.secretKey = secretKey
    s.apiBase = "https://www.bitgo.cn"
    s.timeout = 20 * time.Second
    s.timeLocation = time.FixedZone("Asia/Shanghai", 8*60*60)

    return s
}

func (p *iBitgo) apiCall(method string) (*Json, error) {
    req, err := http.NewRequest("POST", fmt.Sprintf("%s/appApi.html?%s", p.apiBase, method), nil)
    if err != nil {
        return nil, err
    }
    req.Header.Set("Content-Type", "application/x-www-form-urlencoded")
    resp, err := http.DefaultClient.Do(req)
    if err != nil {
        return nil, err
    }
    defer resp.Body.Close()
    b, err := ioutil.ReadAll(resp.Body)
    if err != nil {
        return nil, err
    }
    return NewJson(b)
}

func (p *iBitgo) GetTicker(symbol string) (ticker interface{}, err error) {
    var js *Json
    js, err = p.apiCall("action=market&symbol=" + symbol)
    if err != nil {
        return
    }
    dic := js.Get("data")
    ticker = map[string]interface{}{
        "time": js.Get("time").MustInt64(),
        "buy":  dic.Get("buy").MustFloat64(),
        "sell": dic.Get("sell").MustFloat64(),
        "last": dic.Get("last").MustFloat64(),
        "high": dic.Get("high").MustFloat64(),
        "low":  dic.Get("low").MustFloat64(),
        "vol":  dic.Get("vol").MustFloat64(),
    }
    return
}

func (p *iBitgo) GetDepth(symbol string) (depth interface{}, err error) {
    var js *Json
    js, err = p.apiCall("action=depth&symbol=" + symbol)
    if err != nil {
        return
    }
    dic := js.Get("data")

    asks := [][2]float64{}
    bids := [][2]float64{}

    for _, pair := range dic.Get("asks").MustArray() {
        arr := pair.([]interface{})
        asks = append(asks, [2]float64{toFloat(arr[0]), toFloat(arr[1])})
    }

    for _, pair := range dic.Get("bids").MustArray() {
        arr := pair.([]interface{})
        bids = append(bids, [2]float64{toFloat(arr[0]), toFloat(arr[1])})
    }
    depth = map[string]interface{}{
        "time": js.Get("time").MustInt64(),
        "asks": asks,
        "bids": bids,
    }
    return
}

func (p *iBitgo) GetTrades(symbol string) (trades interface{}, err error) {
    var js *Json
    js, err = p.apiCall("action=trades&symbol=" + symbol)
    if err != nil {
        return
    }
    dic := js.Get("data")
    items := []map[string]interface{}{}
    for _, pair := range dic.MustArray() {
        item := map[string]interface{}{}
        arr := pair.(map[string]interface{})
        item["id"] = toInt64(arr["id"])
        item["price"] = toFloat(arr["price"])
        item["amount"] = toFloat(arr["amount"])
        // trade.Time = toInt64(arr["time"]) * 1000
        if toString(arr["en_type"]) == "bid" {
            item["type"] = "buy"
        } else {
            item["type"] = "sell"
        }
        items = append(items, item)
    }
    trades = items
    return
}

func (p *iBitgo) GetRecords(step int64, symbol string) (records interface{}, err error) {
    var js *Json
    js, err = p.apiCall(fmt.Sprintf("action=kline&symbol=%s&step=%d", symbol, step*60))
    if err != nil {
        return
    }
    items := []interface{}{}
    for _, pair := range js.Get("data").MustArray() {
        arr := pair.([]interface{})
        if len(arr) < 6 {
            err = errors.New("response format error")
            return
        }
        item := [6]interface{}{}
        item[0] = toInt64(arr[0])
        item[1] = toFloat(arr[1])
        item[2] = toFloat(arr[2])
        item[3] = toFloat(arr[3])
        item[4] = toFloat(arr[4])
        item[5] = toFloat(arr[5])

        items = append(items, item)
    }
    records = items
    return
}

func (p *iBitgo) tapiCall(method string, params map[string]string) (js *Json, err error) {
    if params == nil {
        params = map[string]string{}
    }
    params["api_key"] = p.accessKey
    h := md5.New()
    h.Write([]byte(encodeParams(params, false) + "&secret_key=" + p.secretKey))
    params["sign"] = strings.ToUpper(hex.EncodeToString(h.Sum(nil)))
    params["action"] = method
    qs := encodeParams(params, false)

    req, err := http.NewRequest("POST", fmt.Sprintf("%s/appApi.html?%s", p.apiBase, qs), nil)
    if err != nil {
        return nil, err
    }
    req.Header.Set("Content-Type", "application/x-www-form-urlencoded")
    resp, err := http.DefaultClient.Do(req)
    if err != nil {
        return nil, err
    }
    defer resp.Body.Close()
    b, err := ioutil.ReadAll(resp.Body)
    if err != nil {
        return nil, err
    }
    js, err = NewJson(b)
    if js != nil {
        if code := js.Get("code").MustInt64(); code != 200 {
            s := js.Get("msg").MustString()
            if s == "" {
                s = fmt.Sprintf("%v", toString(js.data))
            }
            return nil, errors.New(s)
        }
    }
    return js, err
}

func (p *iBitgo) GetAccount(symbol string) (account interface{}, err error) {
    var js *Json
    js, err = p.tapiCall("userinfo", nil)
    if err != nil {
        return
    }
    mp := js.Get("data")
    assets := map[string]map[string]interface{}{}
    for k := range mp.MustMap() {
        dic := mp.Get(k)
        if k == "free" {
            for c := range dic.MustMap() {
                if _, ok := assets[c]; !ok {
                    assets[c] = map[string]interface{}{}
                }
                assets[c]["currency"] = c
                assets[c]["free"] = dic.Get(c).MustFloat64()
            }
        } else if k == "frozen" {
            for c := range dic.MustMap() {
                if _, ok := assets[c]; !ok {
                    assets[c] = map[string]interface{}{}
                }
                assets[c]["currency"] = c
                assets[c]["frozen"] = dic.Get(c).MustFloat64()
            }
        }
    }
    accounts := []map[string]interface{}{}
    for _, pair := range assets {
        accounts = append(accounts, pair)
    }

    account = accounts
    return
}

func (p *iBitgo) Trade(side string, price, amount float64, symbol string) (orderId interface{}, err error) {
    var js *Json
    js, err = p.tapiCall("trade", map[string]string{
        "symbol": symbol,
        "type":   side,
        "price":  float2str(price),
        "amount": float2str(amount),
    })
    if err != nil {
        return
    }
    orderId = map[string]int64{"id": js.Get("orderId").MustInt64()}
    return
}

func (p *iBitgo) GetOrders(symbol string) (orders interface{}, err error) {
    var js *Json
    js, err = p.tapiCall("entrust", map[string]string{"symbol": symbol})
    if err != nil {
        return
    }
    items := []map[string]interface{}{}
    for _, ele := range js.Get("data").MustArray() {
        mp := ele.(map[string]interface{})
        item := map[string]interface{}{}
        item["id"] = toInt64(mp["id"])
        item["amount"] = toFloat(mp["count"])
        if _, ok := mp["prize"]; ok {
            item["price"] = toFloat(mp["prize"])
        } else {
            item["price"] = toFloat(mp["price"])
        }
        item["deal_amount"] = toFloat(mp["success_count"])

        if toInt64(mp["type"]) == 0 {
            item["type"] = "buy"
        } else {
            item["type"] = "sell"
        }
        item["status"] = "open"
        items = append(items, item)
    }
    return items, nil
}

func (p *iBitgo) GetOrder(orderId int64, symbol string) (order interface{}, err error) {
    var js *Json
    js, err = p.tapiCall("order", map[string]string{"id": toString(orderId)})
    if err != nil {
        return
    }

    found := false
    item := map[string]interface{}{}
    for _, ele := range js.Get("data").MustArray() {
        mp := ele.(map[string]interface{})
        if toInt64(mp["id"]) != orderId {
            continue
        }
        item["id"] = toInt64(mp["id"])
        item["amount"] = toFloat(mp["count"])
        if _, ok := mp["prize"]; ok {
            item["price"] = toFloat(mp["prize"])
        } else {
            item["price"] = toFloat(mp["price"])
        }
        item["deal_amount"] = toFloat(mp["success_count"])

        if toInt64(mp["type"]) == 0 {
            item["type"] = "buy"
        } else {
            item["type"] = "sell"
        }
        switch toInt64(mp["status"]) {
        case 1, 2:
            item["status"] = "open"
        case 3:
            item["status"] = "closed"
        case 4:
            item["status"] = "cancelled"
        }
        found = true
        break
    }
    if !found {
        return nil, errors.New("order not found")
    }
    return item, nil
}

func (p *iBitgo) CancelOrder(orderId int64, symbol string) (ret bool, err error) {
    _, err = p.tapiCall("cancel_entrust", map[string]string{"id": strconv.FormatInt(orderId, 10)})
    if err != nil {
        return
    }
    ret = true
    return
}

type RpcRequest struct {        // 结构体里的字段首字母必须大写,否则无法正常解析,结构体有导出和未导出,大写字母开头为导出。
                                // 在Unmarshal的时候会  根据 json 匹配查找该结构体的tag, 所以此处需要修饰符
    AccessKey string            `json:"access_key"`
    SecretKey string            `json:"secret_key"`
    Nonce     int64             `json:"nonce"`
    Method    string            `json:"method"`
    Params    map[string]string `json:"params"`
}

func OnPost(w http.ResponseWriter, r *http.Request) {
    var ret interface{}
    defer func() {
        if e := recover(); e != nil {
            if ee, ok := e.(error); ok {
                e = ee.Error()
            }
            ret = map[string]string{"error": fmt.Sprintf("%v", e)}
        }
        b, _ := json.Marshal(ret)
        w.Write(b)
    }()

    b, err := ioutil.ReadAll(r.Body)
    if err != nil {
        panic(err)
    }
    var request RpcRequest
    err = json.Unmarshal(b, &request)
    if err != nil {
        panic(err)
    }
    e := newBitgo(request.AccessKey, request.SecretKey)
    symbol := request.Params["symbol"]
    if s := request.Params["access_key"]; len(s) > 0 {
        e.accessKey = s
    }
    if s := request.Params["secret_key"]; len(s) > 0 {
        e.secretKey = s
    }
    if symbolIdx, ok := map[string]int{
        "btc":  1,
        "ltc":  2,
        "etp":  3,
        "eth":  4,
        "etc":  5,
        "doge": 6,
        "bec":  7,
    }[strings.Replace(strings.ToLower(symbol), "_cny", "", -1)]; ok {
        symbol = toString(symbolIdx)
    }
    var data interface{}
    switch request.Method {
    case "ticker":
        data, err = e.GetTicker(symbol)
    case "depth":
        data, err = e.GetDepth(symbol)
    case "trades":
        data, err = e.GetTrades(symbol)
    case "records":
        data, err = e.GetRecords(toInt64(request.Params["period"]), symbol)
    case "accounts":
        data, err = e.GetAccount(symbol)
    case "trade":
        side := request.Params["type"]
        if side == "buy" {
            side = "0"
        } else {
            side = "1"
        }
        price := toFloat(request.Params["price"])
        amount := toFloat(request.Params["amount"])
        data, err = e.Trade(side, price, amount, symbol)
    case "orders":
        data, err = e.GetOrders(symbol)
    case "order":
        data, err = e.GetOrder(toInt64(request.Params["id"]), symbol)
    case "cancel":
        data, err = e.CancelOrder(toInt64(request.Params["id"]), symbol)
    default:
        if strings.HasPrefix(request.Method, "__api_") {
            data, err = e.tapiCall(request.Method[6:], request.Params)
        } else {
            panic(errors.New(request.Method + " not support"))
        }
    }
    if err != nil {
        panic(err)
    }
    ret = map[string]interface{}{
        "data": data,
    }

    return
}

func main() {
    var addr = flag.String("b", "127.0.0.1:6666", "bind addr")
    flag.Parse()
    if *addr == "" {
        flag.Usage()
        return
    }
    basePath := "/exchange"
    log.Println("Running ", fmt.Sprintf("http://%s%s", *addr, basePath), "...")
    http.HandleFunc(basePath, OnPost)
    http.ListenAndServe(*addr, nil)
}


もっと見る

小草パイトン接点プロトコルのカスタマイズされた取引所の例 https://www.fmz.com/strategy/101399

小草Python接点プロトコルのカスタマイズされた交換例https://www.fmz.com/strategy/101399

リウウイ9090bitmexの取引所はプラットフォームにアクセスしていますが, _C ((exchange.GetTicker).Buy とか _C ((exchange.GetDepth).Bids[0]はどちらも購入価格を得ることができず,エラーも返します.なぜかわかりません.

毎日 柔らかい模様ハーハ,GPLのプラグインを書けば,完全に交換APIを連結できるのか,それとも自分のプログラムで直接使うのか?

シュチ751ロボット/戦略/取引所の配置など,完全なプロトコルのアクセス例はありますか? 模倣以上の話,ハハハ

ほらこのコードは,UTPプラグインですよね? このプラグインは,私が直接呼び出すことができる策略模型のようなものではありませんか? 私はpy post またはgetを使って送信し,データを取得し,解析します. このUTPプラグインのコードは, botvsのAPIを受信し,それを解析し,監視アドレスとポートを設定する http://127.0.0.1:6666/DigitalAssetを書き,実際にはhttps://www.bitmex.com/のようなアドレスではありませんか?

シャリーPythonの場合は,これだけです.

ゼーディプラグインを使うことは,多くの関数を自分で書くことを意味するのか,例えばGetAccout (?

アランヤオbotVSはjsの例ではありませんか?なぜjsがないのか?なぜGOがあるのか?

nxtプレーヤー6号機が離陸したが,うまくいかない.

婚約者も野蛮な

発明者 量化 - 微かな夢BITMEXはフューチャー取引所である. 操作または取得する契約を設定するには,APIのドキュメント exchange.SetContractType関数を参照してください.

毎日 柔らかい模様そして,

発明者 量化 - 微かな夢しかし,もし戦略がマルチプラットフォームだったら? 森を1本の木のために捨てるわけにはいかない!

発明者 量化 - 微かな夢実行可能なファイルにコンパイルし,管理者と一緒に実行できます. この記事へのトラックバック一覧です. Python の例はまだないので,時間を取って作ってみました.

ほらプラグインは,それを実行させる方法,例えばpy形式で書き,ホストexeディレクトリに直接置くか?また,ローカルpyプログラムでプラグインのpyファイルディレクトリに参照のパスを追加するか?

発明者 量化 - 微かな夢ポリシーは特別な処理を必要とせず,BotVS apiのインターフェースでは,これらの取引所のオブジェクトは同じである (BotVSがアクセスされているか,またはあなたが自分で書いた通用プロトコルプラグインがサポートされているか),ポリシーは変更する必要はありません. このプラグインと管理者が一緒に動作します. 上記のドキュメントを参照してください: 1 汎用プロトコルプラグインを実行,ポート設定 この記事へのトラックバック一覧です.

ほらこの機能は,自分の書いた後,プラグインに対応する取引所がここに関数を呼び,他の取引所が既存のbotvs apiを使用する策略模型のようです.

発明者 量化 - 微かな夢- この汎用プロトコルプラグインのコードは, botvsのAPIを接続しながら,post、get を解読し,実行しているのですか? - http://127.0.0.1:6666/DigitalAsset このアドレスは,取引所のオブジェクトを代表し,管理者の要求がこのアドレスに送られる.プラグインサービスプログラムは,このアドレス要求を監視し,応答し,管理者の代わりに取引所にアクセスし,管理者の要求されたデータを返します.

発明者 量化 - 微かな夢Pythonで実装したものは同じです インターフェースやフォーマットも同じです 言語の違いだけです.

発明者 量化 - 微かな夢この例は,ホストの外部プラグイン形式で,BotVSプラットフォームへのアクセスのためのいくつかの取引所のインタラクティブコードを書き出すことを目的としています. PHP,node.js,python,Goなどの言語でこのようなプラグインを書き出すことができます.

発明者 量化 - 微かな夢はい,BotVS API GetAccountなどのインターフェース機能の具体的実装を書き出すことは,接続された取引所のコードを自分で書き込むことと同じです.

発明者 量化 - 微かな夢Python,node.js,PHP,Golangなども使用できます.