Type/to search
8
Follow
1364
Followers
O protocolo universal da plataforma de negociação quantitativa do inventor está conectado à bolsa personalizada
Tutorials
Created 2017-08-24 16:29:56  Updated 2021-06-10 15:33:02
 23
 17884

Documentação de uso do protocolo

Pode aceder a qualquer um dos serviços fornecidos pelo protocoloAPIA plataforma de negociação, sem limitação de protocolos de API específicos, sejarestwebsocketfix... pode ser acessado.
Exemplo de protocolo geral do Python: https://www.fmz.com/strategy/101399

  • 1 - Funcionamento de plug-ins de protocolos gerais e configuração de portas

    O endereço e a configuração da porta do plug-in do protocolo GNU para a escuta de áudio.
    Exemplo:http://127.0.0.1:6666/DigitalAssetOu:http://127.0.0.1:6666/exchange

    Por que isso?IPO caminhoTecido de lã?
    Porque emInventores quantificadosPáginasCentro de controleSaltoAdicionar uma bolsaPágina, selecione a barra de protocolo geral, exibindo alémAPI-KEYHá também uma barra de endereço de serviço, que diz ao administrador qual IP e qual porta deve ser acessado (o administrador e o plug-in do protocolo GNU podem não estar no mesmo dispositivo).http://127.0.0.1:6666/DigitalAssetDigitalAssetEste é um nome que se define, mas é apenas um exemplo.

    A página do Inventor Quantum Add Exchange é assim:
    Normalmente, as informações da conta de configuração da bolsa só precisam ser configuradasaccess keyesecret keyNo entanto, algumas interfaces de API de exchanges exigem a transmissão de senhas de transação (por exemplo, as interfaces de encomenda de algumas exchanges), e quando a página de protocolos gerais não tem controles excedentes para escrever essas informações, quando se encontra com uma API de exchanges desse tipo, podemos escrever as informações de configuração excedentes que precisam ser transmitidas emsecret keySe a informação não for sensível, pode ser escrita em:access keyA sequência de comandos é gerada por uma sequência de comandos de um programa de extensão do protocolo GNU.splitA operação consiste em separar os dados, como mostrado no exemplo acima.

    img

    E depois processamos o plugin e obtemosXXX_PassWord
    A função newBitgo, por exemplo, no último exemplo completo deste post:

    mylang
    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

    Plug-ins do GNU em geralmainExemplos de funções:
    GoDescrição:

    mylang
    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) }
  • Função de resposta

    O plug-in do GNU/Linux é um programa de bloqueio de portas designadas para a escuta contínua, para enviar solicitaçõesrequestUma vez que uma solicitação é chamada, a função de resposta executa a resposta e analisa os parâmetros nos 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 有介绍说明。 } */

    Portanto, a estrutura obtida após a reversão de sequenciamento de dados do corpo de solicitação recebido no plugin do GNUrequestEmMethodPodemos usá-lo.switchPara classificar as APIs que processam a quantificação de diferentes inventores, ou seja, identificar quais inventores são chamados pela política que está sendo executada no hostAPI(Chamada por:

    GoExemplo de linguagem:

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

    Essas ramificações executam o retorno de dados escritos na estrutura a ser respondida pelo programa de plug-in do protocolo geral, respondendo às solicitações do administrador.

    Exemplo de linguagem Go:

    mylang
    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 Tipos de chamadas da API

    A maioria das pessoas não sabe o que é um blog.
    Interfaces públicas que não requerem assinatura, como:

    GetTicker()

    GetDepth()

    GetTrades()

    GetRecords(period)
    ...

    1. Interfaces de usuário que requerem assinatura, como:

    BuySell

    GetOrder(id)

    GetOrders()

    GetAccount()

    CancelOrder(id)
    ...
    A forma de assinatura pode variar de uma plataforma para outra, e deve ser especificada de acordo com as necessidades.

  • 4 - Quando os inventores quantificam as chamadas das interfaces das APIsPlug-in de protocolo genéricoeHospedarFormatos de dados interativos:

    Algumas das interfaces de API de inventores de quantidade são:GetName()GetLabel()Equações que não são chamadas paraPlug-in de protocolo genéricoEnviar um pedido.
    exchange.GetName()A chamada de um exchange configurado com um plugin genérico é devolvida para "Exchange".

    • 1、GetTicker: Para obter dados atuais.

      HospedarEnviado para a função de resposta de escutarequestEmmethodPara:ticker

      Parâmetros enviados pelo administrador:request.Params.symbol: Enviado pelo administrador de acordo com a configuração da página do robô.

      O formato de dados que o host solicita que o host carregue quando solicita um plug-in 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 enviado ao host: ((ou seja, o formato de retorno de dados para o host após o plug-in do protocolo GNU solicitar a interface da exchange)

      Estrutura JSON

      { "data": { "time": 1500793319499, // 毫秒时间戳,整型 "buy": 1000, // 以下浮点型 "sell": 1001, "last": 1005, "high": 1100, "low": 980, "vol": 523, } }
    • 2、GetRecords:Para obter dados de linha K fornecidos pela bolsa . . . (obtido de acordo com os parâmetros solicitados pelo administrador)

      A função de resposta enviada pelo administrador àrequestEmmethodPara:records

      Parâmetros enviados pelo administrador:request.Params.periodValores associados:exchange.GetRecordsO primeiro parâmetro da função, na verdaderequest.Params.periodPeríodo expresso em minutos, por exemplo, o ciclo diurno é60*24Agora mesmo1440request.Params.symbol: Os administradores enviam de acordo com as moedas definidas.

      O formato de dados que o host solicita que o host carregue quando solicita um plugin do GNU General Public License

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

      Estrutura de retorno do valor enviado ao administrador final:

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

      Dados do teste de linguagem 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 inventoresLogmostrarrecordsDados:

      [ {"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: 1, o primeiro elemento da matriz de segunda dimensão éintO tipo, representando o tempo <unk>。 2, o administrador automaticamente dá o tempo <unk>。 multiplicado por 1000, acima pode ser visto 。

    • 3、GetDepth:Para obter informações mais detalhadas sobre o mercado de ações, clique aqui: "Orders are thin, sell one, sell two... buy one, buy two"...

      A função de resposta enviada pelo administrador àrequestEmmethodPara:depth

      Parâmetros enviados pelo administrador:request.Params.symbol: Envio de moedas pelo administrador de acordo com a configuração da política

      O formato de dados que o host solicita que o host carregue quando solicita um plugin do GNU General Public License

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

      Estrutura de retorno do valor enviado ao administrador:

      Estrutura 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 registros de transações de toda a bolsa durante um determinado período de tempo fornecidos pela bolsa (não os próprios registros de transações)

      A função de resposta enviada pelo administrador àrequestEmmethodPara:trades

      Parâmetros enviados pelo administrador:request.Params.symbolExemplos de moedas que podem ser negociadas são:btc, enviado pelo administrador de acordo com a estratégia definida.

      O formato de dados que o host solicita que o host carregue quando solicita um plugin do GNU General Public License

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

      Estrutura de retorno do valor enviado ao administrador:

      Estrutura 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:Obter informações sobre os ativos da conta

      A função de resposta enviada pelo administrador àrequestEmmethodPara:accounts

      Parâmetros enviados pelo administrador: ((Observe! geralmente é para obter todos os ativos da conta!, Dependendo da interface da bolsa, é para obter informações sobre o total de ativos separadamente ou para obter informações sobre o total de ativos)

      O formato de dados que o host solicita que o host carregue quando solicita um plugin do GNU General Public License

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

      Estrutura de retorno do valor enviado ao administrador final:

      Estrutura JSON

      { "data": [ {"currency": "btc", "free": 1.2, "frozen": 0.1}, {"currency": "ltc", "free": 25, "frozen": 2.1}, {"currency": "ltc", "free": 25, "frozen": 2.1}, ... ], "raw" : {...} // 可以写入插件访问交易所时,交易所返回的原始信息(response) }
    • 6、Buy、Sell:Envio de encomenda, encomenda de transação.

      A função de resposta enviada pelo administrador àrequestEmmethodPara:trade

      Parâmetros enviados pelo administrador:request.Params.typeO responsável é chamado.exchange.BuyOu...exchange.SellEnviarrequest.Params.priceA estratégia chama:APIO primeiro parâmetro da função,request.Params.amountA estratégia chama:APIO segundo parâmetro da funçãorequest.Params.symbol: Os administradores enviam de acordo com as moedas definidas.

      O formato de dados que o host solicita que o host carregue quando solicita um plugin do GNU General Public License

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

      Estrutura de retorno do valor enviado ao administrador:

      Estrutura JSON

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

      A função de resposta enviada pelo administrador àrequestEmmethodPara:order

      Parâmetros enviados pelo administrador:request.Params.idrequest.Params.symbol

      O formato de dados que o host solicita que o host carregue quando solicita um plugin do GNU General Public License

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

      Estrutura de retorno do valor enviado ao administrador:

      Estrutura JSON

      { "data": { "id": 2565244, "amount": 0.15, "price": 1002, "status": "open", // "open":挂起状态、"closed":完成关闭状态、"cancelled":已取消 "deal_amount": 0, "type": "buy", // "buy"、"sell" "avg_price": 0, // 如果交易所没有提供,在处理时可以赋值为0 } }
    • 8、GetOrders: Obter informações sobre todos os pedidos pendentes

      A função de resposta enviada pelo administrador àrequestO método é:orders

      Parâmetros enviados pelo administrador:request.Params.symbol

      O formato de dados que o host solicita que o host carregue quando solicita um plugin do GNU General Public License

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

      Estrutura de retorno do valor enviado ao administrador:

      Estrutura JSON

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

      A função de resposta enviada pelo administrador àrequestEmmethodPara:cancel

      Parâmetros enviados pelo administrador:request.Params.id: tipo 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 configuração da política

      O formato de dados que o host solicita que o host carregue quando solicita um plugin do GNU General Public License

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

      Estrutura de retorno do valor enviado ao administrador:

      Estrutura JSON

      { "data": true, // true or false }
    • 10、IO: Função de exchange.IO para invocar a plataforma de quantificação do inventor

      A função de resposta enviada pelo administrador àrequestEmmethodPorquê?__api_Nome do método que começa:

      O formato de dados que o host solicita que o host carregue quando solicita um plugin do GNU General Public License

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

      Estrutura de retorno do valor enviado ao administrador:

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

      Por exemplo, a chamada de estratégia:

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

      Código de teste no plugin (em linguagem go):

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

      Linha de comando do plugin:
      2017/08/31 10:19:59 Running http://127.0.0.1:6666/DigitalAsset ...

      Request.Method,request.Params impresso na linha de comando do plugin
      No corpo do pedido enviado pelo administrador, após análise de dados:request.MethodPara:__api_cancel_borrow
      No corpo do pedido enviado pelo administrador, após análise de dados:request.ParamsPara:{"borrow_id" : "123", "symbol" : "cny"}

      Customizações de processamento para acesso direto a essas exchangesAPIdeexchange.IOChamado.

      # 注意: # 在调用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")) ... }
    • Suporte para exchange.GetRawJSON

      Processamento automático de baseexchange.GetRawJSONA invocação de um código-fonte não precisa ser implementada no plugin.

    • Suporte para o exchange.Go

      Processamento automático de baseexchange.GoA invocação de "C" não precisa de ser tratada 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

      O processamento específico precisa ser realizado no programa do plugin, como a configuração de alavanca, código de contrato, direção de encomenda, um registro de variável local pode ser configurado, o acesso ao portfólio requer acesso à interface da bolsa para obter os dados originais e processar a estrutura de portfólio definida na plataforma FMZ e retornar.
      A função recebida pelo plug-in quando a seguinte função é chamada na política:RpcO formato de solicitação é um pouco diferente do de outras interfaces, e é preciso ter em conta que o plug-in do GNURpcRequestA principal diferença entre os params é que os valores são uma estrutura composta.

      • SetContractType
        Configurar o código do contrato

        {"access_key":"123","method":"io","nonce":1623307269528738000,"params":{"args":["quarter"],"code":2},"secret_key":"123"}
      • SetDirection
        Configurar a ordem de entrega.

        {"access_key":"123","method":"io","nonce":1623308734966484000,"params":{"args":["closesell"],"code":1},"secret_key":"123"}
      • SetMarginLevel
        Configurar uma alavanca de futuros.

        {"access_key":"123","method":"io","nonce":1623308734966939000,"params":{"args":[12],"code":0},"secret_key":"123"}
      • GetPosition
        Obtenção de posições em futuros.
        Quandoexchange.GetPosition()Quando chamadas:

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

        Quandoexchange.GetPosition("swap")Quando chamadas:

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

  • Exemplos de linguagem Go com um plugin de protocolo geral (para acessar a bolsa de compra de bitcoins)

mylang
/* 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) }
Related Recommendations
Comment
All comments (23)

    Python接通用协议自定义交易所例子 https://www.fmz.com/strategy/101399

    8 years ago

    Python接通用协议自定义交易所例子https://www.fmz.com/strategy/101399

    8 years ago

    bitmex交易所已经接入平台,但不管用 _C(exchange.GetTicker).Buy 还是 _C(exchange.GetDepth).Bids[0] 都无法取得买一价,而且还会报错。不知道是为什么

    8 years ago

    BITMEX 是期货交易所 需要先设置 你要操作 或者 获取 行情的 合约, 参看 API 文档 exchange.SetContractType 函数

    8 years ago

    哈哈,写一个通用协议插件都可以自己完全对接交易所API了,还不如直接用自己的程序,是不是有点多此一举了。

    8 years ago

    但是 如果 策略是个多平台的呢?不能为了一棵树放弃一片森林啊 ! ^^

    8 years ago

    也对。

    8 years ago

    有没有现成的通用协议接入的例子完整机器人/策略/交易所配置等?讲再多不如模仿,哈哈哈

    8 years ago

    这上面的代码,就是通用协议插件是吧?这个插件不是类似于策略模版那种我直接可以调用?我会用py post或者get 发送和获得数据,并解析。这段通用协议插件的代码,是不是就是实现的post、get和解析,同时对接到botvs的api?编写好的 “通用协议插件” 的监听地址和端口设置这个 http://127.0.0.1:6666/DigitalAsset,实际上是不是类似于 https://www.bitmex.com/ 这种地址,机器人像这个地址发信息?

    8 years ago
    • 这段通用协议插件的代码,是不是就是实现的post、get和解析,同时对接到botvs的api?
      是的。
    • http://127.0.0.1:6666/DigitalAsset
      这个地址是 代表 交易所对象, 托管者 的请求都会 发送到这个地址。 插件服务程序会 监听这个地址请求, 并作出响应,代替托管者去访问交易所,并返回 托管者 要求 的数据(格式),实现 ,策略机器人 兼容某个 BotVS 还没有接入的交易所。
    8 years ago

    这个自己写好后,是不是类似于策略模版,要对接插件对应的交易所就调用这里面的函数,用其他的交易所就用现有的botvs api?

    8 years ago

    策略里面不用特殊的处理 ,对于 BotVS api 接口 , 这些交易所对象都是 一样的(不论是 BotVS 已经接入的,还是 您自己编写 通用协议 插件 支持的),策略不用做任何 调整。 这个 插件 和托管者 一起运行起来 。 就实现了 对这个 交易所的支持。 和 已经接入的 完全一样。
    看下 上面 文档 : 1、 通用协议插件运行、端口设置
    的内容 就明白了。

    8 years ago

    插件是如何让它运行起来,例如编写成py格式的,直接放在托管者exe目录里吗?还在本地py程序里添加引用路径到插件的py文件目录?

    8 years ago

    就是 编译成一个 可执行文件, 和托管者一起运行就可以了
    哪个目录无所谓。
    python 的 例子暂时 还没有,抽时间我写个。

    8 years ago

    有python的就好了,只会这个

    8 years ago

    用 python 实现 也是 一样的 , 接口 格式 都一样
    仅仅是 用的语言不同而已。

    8 years ago

    还是没有完全懂……使用通用程序插件是不是就表示要自己编写很多函数的实现,例如 GetAccout()?

    9 years ago

    是的 , 要编写 实现 BotVS API GetAccount 等 这些 接口 函数的具体实现, 相当于 把自己编写 对接的交易所的代码 衔接到 BotVS 方便 策略统一 调用。这样可以 没有限制的去 参与 任何提供 接口的 交易所交易。

    9 years ago

    botVS不都是js范例多吗?为何没js的?怎么又给GO的?

    9 years ago

    这个是 托管者 外部 插件形式的 范例,目的是 编写一些 交易所 交互 代码 ,对接入 BotVS 平台, 相当于 自己接交易所到BotVS 了。您可以使用 PHP 、node.js 、python 、 Go 等 语言 编写这类的 插件, 只要 数据格式符合以上文档说明格式,插件获取的数据都可以对接到 相应的 BotVS API 接口。

    9 years ago

    6到飞起,然而并不会搞。。。

    9 years ago

    根据例子 , 使用 python 、node.js、PHP 、Golang 都可以接的。

    9 years ago

    牛逼

    9 years ago
  • 1
iPhone Download
Forums
PINE Language
© 2015 - ∞ INVENTOR PTE LTD (SG)