В процессе загрузки ресурсов... загрузка...

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

Автор:Изобретатели количественного измерения - мечты, Создано: 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 могут быть использованы.