Inventores de plataformas de negociação quantitativa de protocolo geral de acesso a exchanges personalizados

Autora:Sonhos pequenos, Criado: 2017-08-24 16:29:56, Atualizado: 2021-06-10 15:33:02

Documentação de uso do Protocolo Geral

O protocolo pode ser usado para acessar qualquer um dosAPIA troca é feita através de um protocolo API específico, sem limites, sejarestwebsocketfix... pode acessar e usar. Exemplos de protocolo Python:https://www.fmz.com/strategy/101399

  • 1, Plug-in do protocolo geral em execução, configuração de portos

    O plug-in de protocolo geral do plug-in do plug-in de protocolo geral do plug-in do plug-in do plug-in do plug-in do plug-in do plug-in do plug-in do plug-in do plug-in do plug-in do plug-in do plug-in do plug-in do plug-in do plug-in do plug-in do plug-in do plug-in do plug-in do plug-in do plug-in do plug-in do plug-in do plug-in. Exemplos:http://127.0.0.1:6666/DigitalAssetOu:http://127.0.0.1:6666/exchange

    Por que colocar essas coisas?P.I.CaminhosE então? Porque não?Inventor de quantificaçãoPáginaCentro de ControleSaltoAdicionar a trocaA página, selecione a barra de protocolos gerais e mostre excetoAPI-KEYO endereço do serviço é o endereço de serviço que diz ao administrador qual IP e porta para acessar (o administrador e o plug-in do protocolo geral não podem ser executados no mesmo dispositivo).http://127.0.0.1:6666/DigitalAssetDigitalAssetEste é um nome auto-definido, mas é apenas um exemplo.

    A página da Inventor Quantitative Added Exchange é a seguinte: Normalmente, as informações de configuração de contas das bolsas só precisam ser configuradas.access keyesecret keyNo entanto, algumas interfaces de API de trocas exigem a transmissão de um código de transação (por exemplo, a interface de baixo de algumas trocas), então, como não há controles excessivos na página do protocolo geral para escrever essas informações, quando encontramos uma API de tais trocas, podemos escrever as informações de configuração excessivas que precisam ser transmitidas emsecret keySe a informação não for sensível, pode ser escrita emaccess keyO que você faz é fazer uma barra de código de código de código de código de código de código.splitA função de separar os dados, como mostrado no exemplo abaixo.

    img

    E depois fazemos o processamento no plugin, obtemosXXX_PassWordNão, não é. Por exemplo, no último exemplo completo desta postagem, a função 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

    Plugins de protocolo geralmainExemplo de função:GoDescrição:

    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, função de resposta

    Plug-in de protocolo geral para o plugin de protocolo para o programa de vigilância constante em uma porta especificada, se houver um pedido enviadorequestQuando uma solicitação é feita, a função de resposta é chamada para executar a resposta e, em seguida, analisar os parâmetros dos dados da solicitação. Os dados da solicitação enviados pelo administrador são:

    /* 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 有介绍说明。
    }
    */
    

    Então, de acordo com a solicitação recebida no plug-in do protocolo geral, a estrutura obtida após a seqüenciação dos dados do corpo contra o JSONrequestO que é?MethodPodemos usarswitchPara classificar o processamento de quantificação de diferentes inventores em uma API (ou seja, identificar as chamadas de políticas executadas no host de qual inventor é a quantificação)API(Chamada para:

    GoExemplos de linguagem:

    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:
          ...
    

    Esses ramos, após a execução dos dados retornados, são escritos na estrutura a ser respondida pelo plug-in do protocolo geral, respondendo aos pedidos do administrador.

    Exemplo de linguagem 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. Tipo de chamadas da API

    A maioria dos blogueiros não tem conhecimento do assunto. 1, Interfaces públicas sem necessidade de assinatura, como:

    GetTicker()

    GetDepth()

    GetTrades()

    GetRecords(period)

    2, Interfaces de usuário que requerem assinatura, como:

    BuySell

    GetOrder(id)

    GetOrders()

    GetAccount()

    CancelOrder(id)O que é isso? O método de assinatura pode variar de acordo com as bolsas e precisa ser escrito de acordo com as necessidades.

  • 4, quando os inventores quantificam as chamadas de cada APIPlugins de protocolo geraleCustódiaO formato de dados interativos:

    Algumas das interfaces de API quantificadas por inventores são:GetName()GetLabel()A função igual não é chamada paraPlugins de protocolo geralEnvie um pedido.exchange.GetName()O "Exchange" é devolvido quando uma chamada é feita para uma bolsa configurada com um plug-in geral.

    • 1, GetTicker:O Google Maps é usado para obter dados atuais sobre o setor.

      CustódiaA função de resposta é enviada para a função de escutarequestO que é?methodPara:ticker

      O servidor envia os parâmetros:request.Params.symbolA partir de agora, o servidor pode enviar o seu pedido de pagamento através de um bot.

      O formato de dados que o sujeito carrega quando o administrador solicita um plugin do protocolo geral (JSON)

      {
          "access_key" : "access_key",
          "secret_key" : "secret_key",
          "nonce" :      "1500793319499",            // 毫秒时间戳
          "method" :     "ticker",
          "params" :     {"symbol" : "ETH_BTC"},     // 以ETH_BTC交易对举例
      }
      

      Estrutura de valor de retorno final enviado ao administrador: (ou seja, o formato em que os dados retornam ao administrador após o plug-in do protocolo geral solicitar a interface da bolsa)

      Estrutura do JSON

      {
          "data": {
              "time": 1500793319499,  // 毫秒时间戳,整型
              "buy": 1000,            // 以下浮点型
              "sell": 1001,
              "last": 1005,
              "high": 1100,
              "low": 980,
              "vol": 523,
          }
      }
      
    • 2, GetRecords:Usado para obter dados da linha K fornecidos pela bolsa.

      O servidor enviou para a função de resposta de vigilânciarequestO que é?methodPara:records

      O servidor envia os parâmetros:request.Params.periodRelacionamento de valores:exchange.GetRecordsO primeiro parâmetro de uma função, realmente.request.Params.periodCiclos representados por minutos, como ciclos diários.60*24Ou seja,1440request.Params.symbolO administrador pode enviar o conteúdo de acordo com a moeda que você escolheu.

      O formato de dados que o sujeito carrega quando o administrador solicita o plugin do protocolo geral

      {
          "access_key" : "access_key",
          "secret_key" : "secret_key",
          "nonce" :      "1500793319499",            // 毫秒时间戳
          "method" :     "records",
          "params" :     {"symbol" : "ETH_BTC", "period" : "1440"},     // 以ETH_BTC交易对,K线周期为日线举例
      }
      

      A estrutura de valores de retorno que é enviada ao administrador final:

      Estrutura do 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],
                  ...
          ]
      }
      

      Os dados do teste de idiomas do 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}
      }
      

      Plataforma de quantificação de inventoresLogDisponívelrecordsInformações:

      [
          {"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}
      ]
      

      Nota: o primeiro elemento de uma matriz em 2D éintTipo, que representa o tempo de tempo. 2, o administrador automaticamente dá o tempo de tempo multiplicado por 1000, ou mais pode ser visto.

    • 3, Profundidade:Obtenha informações mais detalhadas sobre as bolsas (ordens minúsculas, venda um, venda dois... compra um, compra dois...)

      O servidor enviou para a função de resposta de vigilânciarequestO que é?methodPara:depth

      O servidor envia os parâmetros:request.Params.symbol: enviado pelo administrador de acordo com a moeda definida pela política

      O formato de dados que o sujeito carrega quando o administrador solicita o plugin do protocolo geral

      {
          "access_key" : "access_key",
          "secret_key" : "secret_key",
          "nonce" :      "1500793319499",            // 毫秒时间戳
          "method" :     "depth",
          "params" :     {"symbol" : "ETH_BTC"},     // 以ETH_BTC交易对举例
      }
      

      A estrutura de valores de retorno que é enviada ao administrador é:

      Estrutura do 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:Obtenção de registos de transações de uma determinada época (que não sejam os seus próprios registos de transações) disponíveis em toda a bolsa

      O servidor enviou para a função de resposta de vigilânciarequestO que é?methodPara:trades

      O servidor envia os parâmetros:request.Params.symbolO valor é negociado em moedas como:btcA partir de agora, o site pode ser enviado em qualquer moeda que o administrador escolher.

      O formato de dados que o sujeito carrega quando o administrador solicita o plugin do protocolo geral

      {
          "access_key" : "access_key",
          "secret_key" : "secret_key",
          "nonce" :      "1500793319499",            // 毫秒时间戳
          "method" :     "trades",
          "params" :     {"symbol" : "ETH_BTC"},     // 以ETH_BTC交易对举例
      }
      

      A estrutura de valores de retorno que é enviada ao administrador é:

      Estrutura do JSON

      { 
          "data": [
              {
                  "id": 12232153,
                  "time" : 1529919412968,
                  "price": 1000,
                  "amount": 0.5,
                  "type": "buy",             // "buy"、"sell"
              },{
                  "id": 12545664,
                  "time" : 1529919412900,
                  "price": 1001,
                  "amount": 1,
                  "type": "sell",
              },{
                  ...
              }
          ]
      }
      
    • 5, GetAccount:Obtenção de informações sobre a conta

      O servidor enviou para a função de resposta de vigilânciarequestO que é?methodPara:accounts

      Os parâmetros enviados pelo administrador: ((atento! geralmente é para obter todos os ativos da conta!, dependendo da interface da bolsa, é para obter separadamente ou para obter informações de ativos totais)

      O formato de dados que o sujeito carrega quando o administrador solicita o plugin do protocolo geral

      {
          "access_key" : "access_key",
          "secret_key" : "secret_key",
          "nonce" :      "1500793319499",            // 毫秒时间戳
          "method" :     "accounts",
          "params" :     {},                         
      }
      

      A estrutura de valores de retorno que é enviada ao administrador final:

      Estrutura do 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、Comprar、Vender:Envio de encomendas, transações de encomendas.

      O servidor enviou para a função de resposta de vigilânciarequestO que é?methodPara:trade

      O servidor envia os parâmetros:request.Params.typeTrustee: por chamadaexchange.BuyOuexchange.SellA mensagem foi enviada.request.Params.priceO que é chamado de estratégia:APIO primeiro parâmetro de uma função é:request.Params.amountO que é chamado de estratégia:APIO segundo parâmetro da função é:request.Params.symbolO administrador pode enviar o conteúdo de acordo com a moeda que você escolheu.

      O formato de dados que o sujeito carrega quando o administrador solicita o plugin do protocolo geral

      {
          "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举例
      }
      

      A estrutura de valores de retorno que é enviada ao administrador é:

      Estrutura do JSON

      {
          "data": {
              "id": 125456,      // 下单后返回的订单id
                                 // 如果订单id是"asdf346sfasf532"这样的字符串形式
                                 // 此处id也可以是字符串类型
          }
      }
      
    • 7, Obter Ordem:Obter informações sobre pedidos com o número de pedido especificado

      O servidor enviou para a função de resposta de vigilânciarequestO que é?methodPara:order

      O servidor envia os parâmetros:request.Params.idrequest.Params.symbol

      O formato de dados que o sujeito carrega quando o administrador solicita o plugin do protocolo geral

      {
          "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格式
      }
      

      A estrutura de valores de retorno que é enviada ao administrador é:

      Estrutura do 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, Obter ordens:Obtenha informações sobre todas as encomendas pendentes

      O servidor enviou para a função de resposta de vigilânciarequestO método é:orders

      O servidor envia os parâmetros:request.Params.symbol

      O formato de dados que o sujeito carrega quando o administrador solicita o plugin do protocolo geral

      {
          "access_key" : "access_key",
          "secret_key" : "secret_key",
          "nonce" :      "1500793319499",            // 毫秒时间戳
          "method" :     "orders",
          "params" :     {"symbol" : "ETH_BTC"},     // 以ETH_BTC交易对举例
      }
      

      A estrutura de valores de retorno que é enviada ao administrador é:

      Estrutura do JSON

      {
          "data": [{
              "id": 542156,
              "amount": 0.25,
              "price": 1005,
              "deal_amount": 0,
              "type": "buy",      // "buy"、"sell"
              "status": "open",   // "open"
          },{
              ...
          }]
      }
      
    • 9, Cancelar Ordem:Cancelar a encomenda de um pedido com um número de pedido especificado

      O servidor enviou para a função de resposta de vigilânciarequestO que é?methodPara:cancel

      O servidor envia os parâmetros:request.Params.idTipo de string: o primeiro parâmetro da função API chamada pela política.request.Params.symbol:btc (exemplo) enviado pelo administrador de acordo com a moeda definida pela política

      O formato de dados que o sujeito carrega quando o administrador solicita o plugin do protocolo geral

      {
          "access_key" : "access_key",
          "secret_key" : "secret_key",
          "nonce" :      "1500793319499",            // 毫秒时间戳
          "method" :     "cancel",
          "params" :     {"symbol" : "ETH_BTC", "id" : "XXX"},     // 以ETH_BTC交易对,id为"XXX"(同GetOrder函数的参数id一样),举例
      }
      

      A estrutura de valores de retorno que é enviada ao administrador é:

      Estrutura do JSON

      {
          "data": true,        // true or false
      }
      
    • 10 IO:调用发明者量化平台的exchange.IO函数

      O servidor enviou para a função de resposta de vigilânciarequestO que é?methodPara__api_O nome do método que começa por.

      O formato de dados que o sujeito carrega quando o administrador solicita o plugin do protocolo geral

      {
          "access_key" : "access_key",
          "secret_key" : "secret_key",
          "nonce" :      "1500793319499",            // 毫秒时间戳
          "method" :     "__api_XXX",                // XXX为具体交易所的API接口(不包含基地址)
          "params" :     {"borrow_id" : "123", "symbol" : "cny"},      // 具体是传入IO函数的参数
      }
      

      A estrutura de valores de retorno que é enviada ao administrador é:

      {
          "data": {...}       // 具体的接口调用的返回值
      }
      

      Por exemplo, chamadas estratégicas:

      var io_str = exchange.IO("api", "POST", "cancel_borrow", "symbol=cny&borrow_id=123")
      

      O código de teste no plugin ((go linguagem):

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

      A linha de comando do plugin: 2017/08/31 10:19:59 Correndohttp://127.0.0.1:6666/DigitalAsset

      Request.Method,request.Params impresso na linha de comando do pluginNo pedido enviado pelo administrador após a análise dos dados no Body:request.MethodPara:__api_cancel_borrowNo pedido enviado pelo administrador após a análise dos dados no Body:request.ParamsPara:{"borrow_id" : "123", "symbol" : "cny"}

      O processamento pode ser personalizado para essas trocas de acesso diretoAPINão.exchange.IOO que você está fazendo?

      # 注意:
      # 在调用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的支持

      Processamento automático do fundoexchange.GetRawJSONO aplicativo é uma ferramenta que pode ser usada para fazer chamadas de conteúdo, mas não precisa ser implementado em um plugin.

    • 对于exchange.Go的支持

      Processamento automático do fundoexchange.GoO Google Maps é um aplicativo de computador que permite que usuários usem o aplicativo para fazer chamadas, sem necessidade de processá-los no plugin.

      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的时候如果指定了超时时间, 
      #      一定要确保获取到最终的数据,这样申请的并发线程才能回收。
      
    • Suporte para funções de futuros

      Requer a implementação de processamentos específicos no plug-in, como a configuração de alavancagem, código de contrato, direção de ordem, pode ser configurado um registro de variáveis locais, obter a posse precisa acessar a interface do exchange para obter os dados primários e processar a estrutura de posse definida na plataforma FMZ e retornar. Quando a função é chamada na política, o plug-in recebeRpcO formato da solicitação é ligeiramente diferente do de outras interfaces.RpcRequestO formato, que se distingue principalmente pelos valores dos params, é uma estrutura complexa.

      • Tipo de contrato
        O código do contrato foi definido.

        {"access_key":"123","method":"io","nonce":1623307269528738000,"params":{"args":["quarter"],"code":2},"secret_key":"123"}
        
      • SetDirection O que é que isso quer dizer?

        {"access_key":"123","method":"io","nonce":1623308734966484000,"params":{"args":["closesell"],"code":1},"secret_key":"123"}
        
      • Set MarginLevel A partir de agora, o mercado será mais competitivo e mais competitivo.

        {"access_key":"123","method":"io","nonce":1623308734966939000,"params":{"args":[12],"code":0},"secret_key":"123"}
        
      • GetPosition O que você pode fazer para obter a garantia de futuro? Quandoexchange.GetPosition()Quando ligar:

        {"access_key":"123","method":"io","nonce":1623308734967442000,"params":{"args":[],"code":3},"secret_key":"123"}
        

        Quandoexchange.GetPosition("swap")Quando ligar:

        {"access_key":"123","method":"io","nonce":1623308734967442000,"params":{"args":["swap"],"code":3},"secret_key":"123"}
        

  • Plug-in do protocolo geral do exemplo completo da linguagem Go (acessar a troca de bitcoins)

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


Mais.

Ervas daninhasExemplo de troca personalizada do protocolo de acesso Python https://www.fmz.com/strategy/101399

Ervas daninhasExemplo de troca personalizada do protocolo de acesso Python http://www.fmz.com/strategy/101399

Liuwei9090Bitmex já acedeu à plataforma, mas não é possível obter um preço de compra com _C (exchange.GetTicker).Buy ou _C (exchange.GetDepth).

Todos os dias um bom modeloHa ha, escrever um plug-in de protocolo geral pode ser um passo mais do que simplesmente usar o seu próprio programa.

Zhchy751Existe algum exemplo de acesso a protocolo geral pronto, robôs completos / estratégias / configurações de exchanges, etc.?

- O quê?O código acima é um plug-in do protocolo geral, certo? Este plug-in não é semelhante ao que eu posso chamar diretamente? Eu vou enviar e obter dados e resolvê-los usando o py post ou o get. O código do plug-in do protocolo geral, é o post implementado, o get e o decomposto, e também o endereço de monitoramento e a porta do botvs.api?

chalieO Python é bom, só isso.

JadeAinda não entendi completamente... usar um plugin de programa geral significa que você precisa escrever muitas funções sozinho, como o GetAccout (?).

AlanyaoO botVS não é mais um exemplo de js? Por que não é?

Nxtplayer6 voou, mas não conseguiu.

A noiva também.O que é que ele faz?

Sonhos pequenosO BITMEX é uma bolsa de futuros. Para definir os contratos que você deseja operar ou acessar, consulte a documentação da API exchange.SetContractType.

Todos os dias um bom modeloTambém sim.

Sonhos pequenosMas e se a estratégia for multi-plataforma? Não podemos desistir de uma floresta por uma árvore!

Sonhos pequenosO que eu quero dizer é que você pode compilar em um documento executável e executá-lo com um administrador. O que é um catálogo não importa. O exemplo de Python ainda não está disponível, mas vou escrever um.

- O quê?Como é que o plug-in faz com que ele funcione, por exemplo, escrito em formato py, colocado diretamente no diretório exe do administrador?

Sonhos pequenosPara a interface BotVS API, esses objetos são os mesmos (seja o BotVS já acessado ou se você escreveu seu próprio Plugin de Protocolo Geral com suporte), sem a necessidade de fazer qualquer modificação. O Plugin funciona junto com o administrador. Veja acima: Documentação: 1, Plug-in do protocolo geral em execução, configuração de portos O conteúdo é óbvio.

- O quê?A função aqui é similar ao modelo de estratégia, onde a função aqui é chamada pela corretora do plug-in correspondente e a função aqui é chamada pela corretora do botvs API existente.

Sonhos pequenos- O código do plugin do protocolo geral é implementado no post, get e descifrado e também ligado ao API do botvs? Sim, sim. - http://127.0.0.1:6666/DigitalAsset Este endereço representa o objeto da troca, e todas as solicitações do administrador são enviadas para este endereço. O plugin de serviço ouve a solicitação do endereço e responde, substituindo o administrador para acessar a troca e retornar os dados solicitados pelo administrador.

Sonhos pequenosA implementação em Python é a mesma, a interface é a mesma, o formato é o mesmo. A única diferença é a linguagem usada.

Sonhos pequenosEste é um exemplo de um plug-in externo do administrador, cujo objetivo é escrever algum código de interação do exchange para acessar a plataforma BotVS, o que equivale a ligar o exchange para o BotVS. Você pode escrever esse tipo de plug-in usando PHP, node.js, python, Go, etc.

Sonhos pequenosSim, para escrever uma implementação concreta dessas funções de interface, como BotVS API GetAccount, é equivalente a escrever seu próprio código para a bolsa de intercâmbio em que você está ligado.

Sonhos pequenosDe acordo com o exemplo, você pode usar Python, Node.js, PHP e Golang.