발명가 양적 거래 플랫폼 일반 프로토콜 사용자 정의 거래소 접근

저자:작은 꿈, 2017-08-24 16:29:56, 업데이트: 2021-06-10 15:33:02

일반 프로토콜 사용 문서

이 공용 프로토콜을 사용하여 모든 서비스를 이용할 수 있습니다API거래하는 거래소, 특정 API 프로토콜은 제한되지 않습니다.restwebsocketfix이 글은 모든 사람들이 사용할 수 있는 글입니다. 파이썬 일반 프로토콜의 예:https://www.fmz.com/strategy/101399

  • 1., 일반 프로토콜 플러그인 실행, 포트 설정

    잘 작성된 일반 프로토콜 플러그인 의 감시 주소와 포트 설정. 예를 들어:http://127.0.0.1:6666/DigitalAsset또는:http://127.0.0.1:6666/exchange

    왜 이런 것들을 설정해야 할까요?IP경로이봐요. 왜냐하면발명가 양자화페이지제어 센터뛰어내리기거래소 추가이 페이지에서, 일반 프로토콜 박스를 선택하여API-KEY또한 서비스 주소 이 있습니다. 이 서비스 주소는 호스트가 어떤 IP와 포트를 액세스해야하는지 알려줍니다. (호스트와 UTP 플러그인이 동일한 장치에서 실행되지 않을 수 있습니다.)http://127.0.0.1:6666/DigitalAssetDigitalAsset이 이름은 자발적으로 정의된 이름이고, 예를 들어서 설명할 수 있습니다.

    이 사이트의 주식 거래소 페이지에는 다음과 같은 내용이 있습니다. 일반적으로 거래소에서 계정 정보를 구성하는 것은access key그리고secret key그러나 일부 거래소의 API 인터페이스는 거래 암호를 전달하는 것을 요구합니다 (예를 들어 일부 거래소의 아래쪽 인터페이스) 이 경우 일반 프로토콜 페이지에 불필요한 컨트롤이 없기 때문에 이러한 거래소의 API를 만났을 때, 우리는 불필요한 전송이 필요한 구성 정보를secret key이 문서는 정보에 민감하지 않은 경우access key이 문자는 GNU 플러그인에서 문자열로 만들어집니다.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에 대한 배열에 따라 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:
          ...
    

    이 브랜치는 실행 후 반환된 데이터를 UPS 플러그인 프로그램이 응답해야 할 구조에 기록하여 관리자의 요청에 응답합니다.

    이 언어의 예를 들어보죠.

    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 인터페이스 호출을 정량화할 때일반 프로토콜 플러그인그리고관리자이 사이트는 Google+에서 사용되고 있습니다.

    일부 발명가들은 양적 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交易对举例
      }
      

      최종적으로 관리자에게 전송되는 반환 값 구조: (즉, UTP 플러그인 요청 거래소 인터페이스가 데이터를 관리자에게 반환하는 형식)

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

      구글의 언어 테스트 데이터:

      ret_records = []interface{}{
          [6]interface{}{1500793319, 1.1, 2.2, 3.3, 4.4, 5.5}, 
          [6]interface{}{1500793259, 1.01, 2.02, 3.03, 4.04, 5.05}
      }
      

      발명가 양적 플랫폼Log표시records데이터:

      [
          {"Time":1500793319000,"Open":1.1,"High":2.2,"Low":3.3,"Close":4.4,"Volume":5.5},
          {"Time":1500793259000,"Open":1.01,"High":2.02,"Low":3.03,"Close":4.04,"Volume":5.05}
      ]
      

      1차원 행렬의 첫 번째 요소는int타입, 시간 을 나타냅니다. 2 관리자는 자동으로 시간 을 1000, 이상으로 곱합니다.

    • 3, 깊이를 얻으십시오:거래소에 대한 자세한 정보를 얻으세요 (오더가 작고, 팔고, 팔고, 살고, 살고...)

      이 랩탑은request그 중method다음을 위해:depth

      관리자가 전송하는 매개 변수:request.Params.symbol: 관리자가 정책 설정에 따라 송금하는 통화

      관리자가 일반 프로토콜 플러그인을 요청할 때 주체가 가지고 있는 데이터 형식을 요청합니다.

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

      결국, 관리자에게 전송되는 반환 값 구조:

      JSON 구조

      {
          "data" : {
              "time" : 1500793319499,
              "asks" : [ [1000, 0.5], [1001, 0.23], [1004, 2.1], ... ],
              "bids" : [ [999, 0.25], [998, 0.8], [995, 1.4], ... ],
          }
      }
      
    • 4,GetTrades:거래소에서 제공되는 거래 기록 (자기 거래 기록이 아닌) 을 거래소 전체의 일정 시간 동안 얻습니다.

      이 랩탑은request그 중method다음을 위해:trades

      관리자가 전송하는 매개 변수:request.Params.symbol이 자금의 가치는 다음과 같습니다.btc이 사이트의 주식들은, 주식 관리자들에 의해 정책 설정에 따라 송금됩니다.

      관리자가 일반 프로토콜 플러그인을 요청할 때 주체가 가지고 있는 데이터 형식을 요청합니다.

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

      결국, 관리자에게 전송되는 반환 값 구조:

      JSON 구조

      { 
          "data": [
              {
                  "id": 12232153,
                  "time" : 1529919412968,
                  "price": 1000,
                  "amount": 0.5,
                  "type": "buy",             // "buy"、"sell"
              },{
                  "id": 12545664,
                  "time" : 1529919412900,
                  "price": 1001,
                  "amount": 1,
                  "type": "sell",
              },{
                  ...
              }
          ]
      }
      
    • 5、GetAccount:계정 자산 정보를 얻으십시오

      이 랩탑은request그 중method다음을 위해:accounts

      관리자가 파러미터를 전송합니다: ((주의! 일반적으로 계정의 모든 자산을 얻습니다!, 거래소 인터페이스에 따라 개별적으로 얻거나 전체 자산 정보를 얻습니다.)

      관리자가 일반 프로토콜 플러그인을 요청할 때 주체가 가지고 있는 데이터 형식을 요청합니다.

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

      결국 관리자에게 전송되는 값 구조를 반환합니다:

      JSON 구조

      {
          "data": [
              {"currency": "btc", "free": 1.2, "frozen": 0.1},
              {"currency": "ltc", "free": 25, "frozen": 2.1},
              {"currency": "ltc", "free": 25, "frozen": 2.1},
              ...
          ],
          "raw" : {...}             // 可以写入插件访问交易所时,交易所返回的原始信息(response)
      }
      
    • 6, 구매, 판매:주문을 보내거나 거래를 주문합니다.

      이 랩탑은request그 중method다음을 위해:trade

      관리자가 전송하는 매개 변수:request.Params.type관리자: 호출에 따라exchange.Buy아니면exchange.Sell이 글은 전 세계 모든 나라들의 글입니다.request.Params.price전략에서 호출:API함수의 첫 번째 매개변수는request.Params.amount전략에서 호출:API이 함수의 두 번째 변수는request.Params.symbol: 관리자가 설정된 통화에 따라 전송합니다.

      관리자가 일반 프로토콜 플러그인을 요청할 때 주체가 가지고 있는 데이터 형식을 요청합니다.

      {
          "access_key" : "access_key",
          "secret_key" : "secret_key",
          "nonce" :      "1500793319499",            // 毫秒时间戳
          "method" :     "trade",
          "params" :     {
                             "symbol" : "ETH_BTC", 
                             "type" : "buy", 
                             "price" : "1000", 
                             "amount" : "1"
                         },                          // 以ETH_BTC交易对,"type":"buy"买请求,价格1000,数量1举例
      }
      

      결국, 관리자에게 전송되는 반환 값 구조:

      JSON 구조

      {
          "data": {
              "id": 125456,      // 下单后返回的订单id
                                 // 如果订单id是"asdf346sfasf532"这样的字符串形式
                                 // 此处id也可以是字符串类型
          }
      }
      
    • 7、GetOrder:지정된 주문 번호의 주문 정보를 얻으십시오

      이 랩탑은request그 중method다음을 위해:order

      관리자가 전송하는 매개 변수:request.Params.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、GetOrders:완료되지 않은 모든 주문 정보를 얻습니다.

      이 랩탑은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/10:调用发明者量化平台的exchange.IO函数

      이 랩탑은request그 중method__api_이 글의 제목은 "Method Name"입니다.

      관리자가 일반 프로토콜 플러그인을 요청할 때 주체가 가지고 있는 데이터 형식을 요청합니다.

      {
          "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 러닝http://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요청 형식은 다른 인터페이스와 약간 다릅니다.RpcRequest이 형식은, 주로 params의 값으로 구분되는 복합 구조이다.

      • SetContractType
        계약 코드를 설정합니다.

        {"access_key":"123","method":"io","nonce":1623307269528738000,"params":{"args":["quarter"],"code":2},"secret_key":"123"}
        
      • 설정 방향 유래상품의 아래쪽을 단방향으로 설정합니다.

        {"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"}
        
      • 이 문서는 훗날 미래에 대한 지분을 취득합니다. 그리고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)
}


더 많은

초목파이썬 GUI 프로토콜 사용자 정의 거래소 예제 https://www.fmz.com/strategy/101399

초목파이썬 연결 프로토콜 사용자 정의 거래소 예 http://www.fmz.com/strategy/101399

류웨이9090bitmex 거래소는 이미 플랫폼에 접속했지만 _C (exchange.GetTicker).Buy 또는 _C (exchange.GetDepth) 를 사용하진 않습니다.

매일매일 예쁘다하하, GNU 플러그인을 작성하면 거래소 API를 완전히 연결할 수 있습니다.

치751보트/전략/거래소 구성 등에 대한 완전한 보트/전략/거래소 구성 등에 대한 일반적인 프로토콜 접근의 예가 준비되어 있습니까?

이 코드는, 보트vs의 API를 작성한 보트프로그램 플러그인의 감시 주소와 포트를 설정하는 동시에 실행되는 post, get 및 decrypted 코드가 아닌가요? http://127.0.0.1:6666/DigitalAsset, http://127.0.0.1:6666/DigitalAsset, https://www.bitmex.com/와 비슷한 코드가 아닌가요?

자, 이제, Python의 경우,

자이드아직 완전히 이해되지 않았습니다... 퍼즐 플러그인을 사용하는 것은 GetAccout 같은 많은 함수를 직접 작성해야하는 구현을 의미합니까?

알란야오botVS는 js의 경우가 아닙니다. 왜 js가 없나요? 왜 GO가 있을까요?

nxtplayer6번 쏘아 올렸지만, 끝내지 않았다.

신부도 마찬가지입니다.강인함

작은 꿈BITMEX는 선물 거래소입니다. 먼저 설정해야 합니다. 당신이 작동하거나 접근하려는 계약은 API 문서 exchange.SetContractType 기능을 참조하십시오.

매일매일 예쁘다또한 ᅳ

작은 꿈하지만 만약 전략이 멀티 플랫폼이라면 어떻게 될까요? 나무 하나 때문에 숲을 포기할 수는 없습니다!

작은 꿈즉, 실행 가능한 문서로 컴파일하여 관리자와 함께 실행할 수 있습니다. 어떤 카테고리인지 중요하지 않습니다. 파이썬의 예는 아직 없는데, 시간이 있으면 하나 써보겠습니다.

플러그인이 어떻게 작동하는지, 예를 들어 py 형식으로 작성하여 호스트 exe 디렉토리에 직접 배치합니까? 또한 로컬 py 프로그램에서 플러그인 py 파일 디렉토리에 참조 경로를 추가합니까?

작은 꿈이 정책은 특별한 처리가 필요하지 않으며 BotVS api 인터페이스의 경우, 이 거래소 객체는 동일합니다. BotVS가 액세스되었든 또는 당신이 직접 작성한 일반 프로토콜 플러그인이 지원되었든, 정책에는 아무런 조정이 필요하지 않습니다. 이 플러그인은 관리자와 함께 실행됩니다. 이 거래소에 대한 지원이 실현됩니다. 이미 액세스 된 것과 완전히 동일합니다. 위 문서를 참조하십시오: 1, GPL 플러그인을 실행, 포트 설정 이 글의 내용은 이해가 됩니다.

이 자체 작성 후, 플러그인을 연결하는 거래소가 여기에 함수를 호출하고 다른 거래소가 기존 botvs api를 사용하는 전략 모형과 비슷합니까?

작은 꿈- 이 UTP 플러그인의 코드는 botvs의 API를 연결하는 동시에 실행되는 post, get 및 decrypt을 수행합니까? 네, 그렇습니다. - http://127.0.0.1:6666/DigitalAsset 이 주소는 거래소 객체를 대표하는 주소로, 관리자의 요청은 이 주소로 전송된다. 플러그인 서비스는 이 주소 요청에 귀를 기울이고, 응답하여 관리자의 대신 거래소를 방문하고 관리자의 요청된 데이터를 반환한다.

작은 꿈파이썬으로 구현한 것과 동일합니다. 인터페이스와 형식도 동일합니다. 이 문헌은 한글에 쓰여진 문헌으로 구성되어 있습니다.

작은 꿈이것은 호스팅하는 외부 플러그인 형태의 예시로, BotVS 플랫폼에 액세스하는 데 대한 일부 거래소 상호 작용 코드를 작성하기 위한 것입니다. 당신은 PHP, node.js, python, Go 등의 언어를 사용하여 이러한 종류의 플러그인을 작성할 수 있습니다. 데이터 형식이 위의 문서 설명 형식과 일치하는 한, 플러그인이 얻은 데이터는 해당 BotVS API 인터페이스에 연결될 수 있습니다.

작은 꿈예, BotVS API를 구현하는 GetAccount과 같은 이러한 인터페이스 기능의 구체적인 구현을 작성하는 것은 연결된 거래소의 코드를 작성하는 것과 같습니다. BotVS에 연결하여 편리하게 통일 정책을 호출하십시오.

작은 꿈예를 들어, 파이썬, 노드.js, PHP, 골랑을 사용할 수 있습니다.