Изобретатели квантовой платформы общих протоколов доступа к пользовательским биржам

Автор:Маленькие мечты, Создано: 2017-08-24 16:29:56, Обновлено: 2021-06-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

    Почему это нужно?ИППутьЧто это? Потому чтоИзобретатель количественныйСтраницаЦентр управленияПрыжкиДобавить биржуНа странице, выберите кнопку "Общие протоколы", показывающую толькоAPI-KEY, а также адресная строка сервиса, которая сообщает администратору о том, на какой IP и порту он должен получить доступ (администратор и плагин-программа не могут работать на одном устройстве).http://127.0.0.1:6666/DigitalAssetDigitalAssetЭто самоопределённое название, и это всего лишь пример.

    На странице обменных площадок Quantitative Inventor's Add-on выглядит так: Обычно для конфигурации учетной записи на бирже требуется только конфигурацияaccess keyиsecret keyОднако API-интерфейсы некоторых бирж требуют передачи пароля сделки (например, нижний интерфейс некоторых бирж), поэтому, поскольку на странице общих протоколов нет лишних контролей для записи этой информации, когда мы сталкиваемся с API таких бирж, мы можем записать лишнюю конфигурационную информацию, которая должна быть передана.secret keyЕсли информация не является конфиденциальной, она может быть написана вaccess keyИ затем вы делаете строку в плагинах General Protocol.splitВ этом случае, если вы хотите, чтобы ваши данные были разделены, используйте эту операцию, как показано на примере.

    img

    Затем обрабатываем в плагинах, получаем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
    }
    

    img

    Общие плагины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)
    }
    
  • 2, функция ответа

    Протокольный плагин-программа для прослушивания на определенном порту, отправляется ли запрос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

    В основном они делятся на две категории: 1, общедоступные интерфейсы без подписи, такие как:

    GetTicker()

    GetDepth()

    GetTrades()

    GetRecords(period)

    2) пользовательский интерфейс, требующий подписи, например:

    BuySell

    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То есть1440request.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}
      ]
      

      Примечание: первый элемент в матрице второго измеренияintТип, который представляет собой время нажатия. 2.

    • 3, Получить глубину:Получить более подробную информацию об биржах (отсутствие заказов, продажа 1, продажа 2... покупка 1, покупка 2...)

      В данном случае, мы можем использовать функцию, которую высылает администратор для ответа на прослушку.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, Получитьаккаунт:Доступ к информации об активах счета

      В данном случае, мы можем использовать функцию, которую высылает администратор для ответа на прослушку.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, Купить, Продать:Отправка заказов, заказ сделок.

      В данном случае, мы можем использовать функцию, которую высылает администратор для ответа на прослушку.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, Получить заказ:Получить информацию о заказе с указанным номером заказа

      В данном случае, мы можем использовать функцию, которую высылает администратор для ответа на прослушку.requestВнутриmethodДля:order

      Администратор отправляет параметры:request.Params.idrequest.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, Получить приказы:Получить информацию обо всех незавершенных заказах

      В данном случае, мы можем использовать функцию, которую высылает администратор для ответа на прослушку.requestМетод: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, Отменить заказ:Отменить заказ с указанием номера заказа

      В данном случае, мы можем использовать функцию, которую высылает администратор для ответа на прослушку.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 язык):

      fmt.Println("request.Method:", request.Method, "request.Params:", request.Params)
      

      Вставка командной строки: 2017/08/31 10:19:59 Runninghttp://127.0.0.1:6666/DigitalAsset

      Request.Method,request.Params, напечатанные в командной строке плагинаВ запросе, отправленном администратором после анализа данных в Body:request.MethodДля:__api_cancel_borrowВ запросе, отправленном администратором после анализа данных в Body: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)
      

      img

      img

      # 注意:使用exchange.Go在wait的时候如果指定了超时时间, 
      #      一定要确保获取到最终的数据,这样申请的并发线程才能回收。
      
    • Поддержка фьючерсных функций

      Необходимо реализовать конкретные обработки в плагинах, такие как настройка рычага, код контракта, направление заказа, можно настроить запись локальных переменных, получить позиции, нужно получить доступ к интерфейсу биржи, получить исходные данные и обработать структуру позиций, определенную на платформе FMZ, и вернуться. Когда в политике вызывается следующая функция, плагин получаетRpcФормат запроса немного отличается от других интерфейсов, необходимо обратить внимание на плагин-программу в General ProtocolRpcRequestФормат, в основном отличающийся парами, является сложной структурой.

      • 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"}
        
      • Установка уровня границы Настройка фьючерсного рычага.

        {"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 с плагинами общих протоколов (доступ к биткойн-обменникам)

/*
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)
}


Больше

ТраваПример обменной системы с использованием протокола Python Access.

ТраваПример обменной системы с использованием протокола Python http://www.fmz.com/strategy/101399

Лиувей9090Битмекс присоединился к платформе, но не смог получить покупку с помощью _C (exchange.GetTicker).Buy или _C (exchange.GetDepth).

Каждый день.Ха-ха, написать плагин-протокол общих протоколов можно самостоятельно, полностью соединяя API бирж, или лучше просто использовать собственные программы.

Zhchy751Есть ли примеры готовых доступных протоколов, полных роботов/стратегий/конфигурации бирж и т.д.?

ДюхуЭтот код - это плагин-протокол, не так ли? Этот плагин не похож на плагин-протокол, который я могу прямо вызвать? Я отправляю и получаю данные и анализирую их с помощью py post или get. Этот плагин-протокол, который выполняет пост, получает и расшифровывает, а также получает botvs.api?

кальянЕсли у вас есть Python, это хорошо.

соломинкаНе совсем понятно... Использование плагинов универсальных программ означает ли, что вам нужно написать много функций самостоятельно, например, GetAccout)))?

АланьяоbotVS - это не больше, чем примеры js? Почему нет js?

nxtplayer6 лет летят, но ничего не делают.

И невесты тоже.Ожесточенный

Маленькие мечтыBITMEX - это фьючерсная биржа. Для того, чтобы получить доступ к контрактам, которые вы хотите использовать или получить, см. API-документы exchange.SetContractType.

Каждый день.И это правда.

Маленькие мечтыНо что, если стратегия будет мультиплатформенной?

Маленькие мечтыИ это означает, что вы можете скомпилировать его в исполняемый файл, который вы можете запустить вместе с хостером. Какой каталог не имеет значения. Я не знаю, как это сделать, но у меня есть несколько примеров Python, которые я написал.

ДюхуКак плагин работает, например, записывается в формате py, прямо в каталоге exe хостера? Или добавляется ссылка на путь в каталог файлов py в локальной программе py?

Маленькие мечтыВ политике нет особой обработки, для интерфейса BotVS API, объекты этих бирж одинаковы (независимо от того, доступны ли BotVS или поддерживаются ваши собственные плагины), политика не требует каких-либо изменений. Смотрите документацию выше: 1, запуск плагина общих протоколов, настройка портов В этом случае мы не можем сказать, что мы не знаем.

ДюхуЭто похоже на шаблон стратегии, где экспозиция, которая хочет связаться с плагином, вызывает эту функцию, а другие экспозиции используют существующий botvs api.

Маленькие мечты- Код этого универсального протокола плагинов, который был реализован в post, get и декодируется, а также соединяется с API botvs? Да, это так. - http://127.0.0.1:6666/DigitalAsset Этот адрес представляет собой объект биржи, к которому отправляются запросы администратора. Плагин-сервис прослушивает запрос на этот адрес и реагирует, чтобы вместо администратора посетить биржу и вернуть запрошенные администратором данные (формат), реализация, стратегия бота совместимы с какой-либо BotVS и еще не подключенной бирже.

Маленькие мечтыЭто то же самое, что и в Python, интерфейс, формат. В этом случае, если вы не знаете языка, то вы не знаете языка, который используется.

Маленькие мечтыЭто пример формы внешнего хост-плагина, целью которого является написание некоторого интерактивного кода биржи для доступа к платформе BotVS. Вы можете использовать языки PHP, node.js, python, Go и т. д. Для написания такого плагина, при условии, что формат данных соответствует формату, описанному выше, данные, полученные плагином, могут быть связаны с соответствующим интерфейсом BotVS API.

Маленькие мечтыДа, для написания конкретных реализаций таких функций интерфейса, как BotVS API GetAccount, достаточно написать код для связанной биржи.

Маленькие мечтыНапример, Python, Node.js, PHP и Golang могут быть использованы.