avatar of 发明者量化-小小梦 发明者量化-小小梦
fokus pada mesej peribadi
4
fokus pada
1271
Pengikut

Protokol universal platform dagangan kuantitatif pencipta disambungkan kepada pertukaran tersuai

Dicipta dalam: 2017-08-24 16:29:56, dikemas kini pada: 2021-06-10 15:33:02
comments   23
hits   17429

Dokumen penggunaan PKP

Boleh menggunakan protokol ini untuk mengakses mana-mana perkhidmatanAPITidak terhad kepada pertukaran, protokol API tertentu, sama adarestwebsocketfix… boleh diakses. Contoh Protokol Umum Python: https://www.fmz.com/strategy/101399

  • ### 1. menjalankan plugin GPRS, setup port

Menulis alamat penyadapan dan tetapan port untuk plugin Perkongsian Perkongsian Perkongsian Perkongsian. Contoh:http://127.0.0.1:6666/DigitalAssetatau:http://127.0.0.1:6666/exchange

Mengapa ia perlu diletakkan?IPLaluanKain bulu? Kerana diPencipta KuantitiHalamanPusat KawalanMelompatTambah bursaHalaman, pilih tetingkap GPRS, dan tunjukkan selainAPI-KEYTerdapat juga kotak Alamat Perkhidmatan, yang memberitahu tuan rumah IP dan port yang harus diakses (pemacu dan pemalam protokol umum tidak boleh berjalan pada peranti yang sama).http://127.0.0.1:6666/DigitalAssetDigitalAssetIni adalah nama yang anda tentukan sendiri, dan ini hanya contohnya.

Ini adalah laman web penambah kuantifikasi pencipta: Biasanya, maklumat akaun konfigurasi bursa hanya perlu dikonfigurasiaccess keydansecret keyTetapi beberapa API pertukaran yang memerlukan penghantaran kata laluan perdagangan (seperti antara muka pesanan di beberapa pertukaran), dan ketika itu kerana halaman protokol umum tidak mempunyai alat kawalan yang berlebihan untuk menulis maklumat ini, apabila kita menemui API pertukaran seperti itu, kita dapat menulis maklumat konfigurasi yang berlebihan yang perlu dihantar disecret keyJika maklumat itu tidak sensitif, ia boleh ditulis dalamaccess key), dan kemudian melakukan senar dalam plugin GPLsplitOperasi, memisahkan data ini, seperti yang ditunjukkan dalam contoh di bawah.

Protokol universal platform dagangan kuantitatif pencipta disambungkan kepada pertukaran tersuai

Kemudian anda boleh mengeditnya di dalam plugin, dan anda akan mendapatXXX_PassWord。 Sebagai contoh, dalam contoh lengkap terakhir dalam catatan ini, fungsi 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
  }

Protokol universal platform dagangan kuantitatif pencipta disambungkan kepada pertukaran tersuai

Plugin Perkongsian PerkongsianmainContoh fungsi: GoPenerangan bahasa:

  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. Fungsi tindak balas

Program penyekat plug-in yang tidak berhenti di pelabuhan yang ditetapkan untuk mendengar, adakah ada permintaan untuk dihantarrequestApabila ada permintaan, fungsi tindak balas dipanggil untuk melaksanakan tindak balas, kemudian menguraikan parameter dalam data permintaan, dan data permintaan yang dihantar oleh tuan rumah adalah:

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

Oleh itu, struktur yang diperoleh selepas pengurutan JSON data badan permintaan yang diterima dalam program plugin GPOrequestDalamMethodKita boleh gunakanswitchUntuk mengelaskan API yang berskala yang berurusan dengan pencipta yang berlainan, iaitu mengenal pasti strategi yang dijalankan pada hos yang dipanggil oleh pencipta yang berskalaAPIPanggilan:

GoContoh bahasa:

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

Bahagian-bahagian ini menjalankan data yang dikembalikan ke dalam struktur yang akan ditanggapi oleh program tambahan protokol umum, untuk menjawab permintaan penjaga.

Contoh bahasa 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. Jenis panggilan API

Terdapat dua kategori: 1. Antara muka awam yang tidak memerlukan tandatangan, seperti:

GetTicker()

GetDepth()

GetTrades()

GetRecords(period)

  1. Antara muka pengguna yang memerlukan tandatangan, seperti:

BuySell

GetOrder(id)

GetOrders()

GetAccount()

CancelOrder(id) … Tanda tangan mungkin berbeza di setiap bursa dan perlu ditulis mengikut keperluan.

  • ### 4. Pencipta mengkuantifikasi setiap panggilan APIPlug-in Protokol UmumdanhosFormat data interaktif:

Beberapa pencipta API kuantitatif seperti:GetName()GetLabel()Fungsi yang sama, tidak dipanggil kePlug-in Protokol UmumMenghantar permintaan. exchange.GetName()“Exchange” akan dikembalikan apabila dipanggil oleh pertukaran yang dikonfigurasi dengan plugin umum.

  • 1、GetTicker: Untuk mendapatkan maklumat terkini.

    hosyang dihantar kepada fungsi responrequestDalammethodUntuk:ticker

    Pengurus menghantar parameter:request.Params.symbol: Dikirimkan oleh pentadbir mengikut tetapan halaman bot.

    Format data yang diminta oleh tuan rumah untuk dibawa oleh tuan rumah apabila meminta plugin protokol umum (JSON)

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

    Struktur nilai pulangan yang akhirnya dihantar kepada tuan rumah: ((iaitu format data yang dikembalikan kepada tuan rumah selepas plugin protokol umum meminta antara muka bursa)

    Struktur JSON

    {
        "data": {
            "time": 1500793319499,  // 毫秒时间戳,整型
            "buy": 1000,            // 以下浮点型
            "sell": 1001,
            "last": 1005,
            "high": 1100,
            "low": 980,
            "vol": 523,
        }
    }
    
  • 2、GetRecords:Data K-Line yang disediakan oleh bursa. Diambil mengikut parameter yang diminta oleh pentadbir

    Pengurus dihantar kepada fungsi respon mendengarrequestDalammethodUntuk:records

    Pengurus menghantar parameter:request.Params.periodNilai berkaitan:exchange.GetRecordsParameter pertama fungsi, sebenarnyarequest.Params.periodTempoh yang dinyatakan dalam minit, contohnya, tempoh hari adalah60*24Sekarang ni1440request.Params.symbol: Pengurus menghantar mata wang mengikut tetapan.

    Format data yang diminta oleh hos untuk dibawa oleh tuan rumah apabila meminta pemalam protokol

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

    Struktur nilai balik yang akhirnya dihantar kepada pentadbir:

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

    Data ujian bahasa 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}
    }
    

    Platform Kuantiti PenciptaLogtunjukrecordsMaklumat:

    [
        {"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, unsur pertama dalam susunan 2 dimensi ialahintTipe, yang mewakili masa 。 2. Pengurus akan secara automatik memberikan masa 乘以1000, di atas boleh dilihat 。

  • 3、GetDepth:Maklumat terperinci mengenai bursa: Perintah Rendah, Jual Satu, Jual Dua, Beli Satu, Beli Dua…

    Pengurus dihantar kepada fungsi respon mendengarrequestDalammethodUntuk:depth

    Pengurus menghantar parameter:request.Params.symbol: Diserahkan oleh pentadbir mengikut tetapan dasar

    Format data yang diminta oleh hos untuk dibawa oleh tuan rumah apabila meminta pemalam protokol

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

    Struktur nilai balik yang akhirnya dihantar kepada pentadbir:

    Struktur 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:Mendapatkan rekod dagangan seluruh pertukaran yang disediakan oleh bursa dalam jangka masa tertentu (bukan rekod dagangan sendiri)

    Pengurus dihantar kepada fungsi respon mendengarrequestDalammethodUntuk:trades

    Pengurus menghantar parameter:request.Params.symbolContoh mata wang yang boleh diperdagangkan:btc, yang dihantar oleh pentadbir mengikut set strategi.

    Format data yang diminta oleh hos untuk dibawa oleh tuan rumah apabila meminta pemalam protokol

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

    Struktur nilai balik yang akhirnya dihantar kepada pentadbir:

    Struktur 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:Mendapatkan maklumat aset akaun

    Pengurus dihantar kepada fungsi respon mendengarrequestDalammethodUntuk:accounts

    Parameter yang dihantar oleh pentadbir: ((Perhatian! Secara amnya adalah mendapatkan semua aset akaun!, bergantung kepada antara muka bursa, adalah mendapatkan maklumat aset secara berasingan atau mendapatkan maklumat aset keseluruhan)

    Format data yang diminta oleh hos untuk dibawa oleh tuan rumah apabila meminta pemalam protokol

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

    Struktur nilai balik yang akhirnya dihantar kepada pentadbir:

    Struktur 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:Menghantar surat tempahan, pesanan transaksi.

    Pengurus dihantar kepada fungsi respon mendengarrequestDalammethodUntuk:trade

    Pengurus menghantar parameter:request.Params.typePengurus mengikut panggilan:exchange.Buyatauexchange.SellPenghantaranrequest.Params.priceDi dalam strategi:APIArgumennya ialah:request.Params.amountDi dalam strategi:APIParameter kedua untuk fungsirequest.Params.symbol: Pengurus menghantar mata wang mengikut tetapan.

    Format data yang diminta oleh hos untuk dibawa oleh tuan rumah apabila meminta pemalam protokol

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

    Struktur nilai balik yang akhirnya dihantar kepada pentadbir:

    Struktur JSON

    {
        "data": {
            "id": 125456,      // 下单后返回的订单id
                               // 如果订单id是"asdf346sfasf532"这样的字符串形式
                               // 此处id也可以是字符串类型
        }
    }
    
  • 7、GetOrder:Mendapatkan maklumat mengenai nombor pesanan yang telah ditetapkan

    Pengurus dihantar kepada fungsi respon mendengarrequestDalammethodUntuk:order

    Pengurus menghantar parameter:request.Params.idrequest.Params.symbol

    Format data yang diminta oleh hos untuk dibawa oleh tuan rumah apabila meminta pemalam protokol

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

    Struktur nilai balik yang akhirnya dihantar kepada pentadbir:

    Struktur 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: Dapatkan semua maklumat pesanan yang belum selesai

    Pengurus dihantar kepada fungsi respon mendengarrequestMethod di sini ialah:orders

    Pengurus menghantar parameter:request.Params.symbol

    Format data yang diminta oleh hos untuk dibawa oleh tuan rumah apabila meminta pemalam protokol

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

    Struktur nilai balik yang akhirnya dihantar kepada pentadbir:

    Struktur JSON

    {
        "data": [{
            "id": 542156,
            "amount": 0.25,
            "price": 1005,
            "deal_amount": 0,
            "type": "buy",      // "buy"、"sell"
            "status": "open",   // "open"
        },{
            ...
        }]
    }
    
  • 9、CancelOrder: Batalkan penghantaran pesanan dengan nombor pesanan yang ditetapkan

    Pengurus dihantar kepada fungsi respon mendengarrequestDalammethodUntuk:cancel

    Pengurus menghantar parameter:request.Params.id: jenis senar, argumen pertama fungsi API yang dipanggil oleh polisi,request.Params.symbol:btc (contoh) dihantar oleh pentadbir mengikut tetapan dasar

    Format data yang diminta oleh hos untuk dibawa oleh tuan rumah apabila meminta pemalam protokol

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

    Struktur nilai balik yang akhirnya dihantar kepada pentadbir:

    Struktur JSON

    {
        "data": true,        // true or false
    }
    
  • 10、IO: Fungsi exchange.IO yang memanggil platform kuantifikasi pencipta

    Pengurus dihantar kepada fungsi respon mendengarrequestDalammethodUntuk__api_Nama kaedah yang bermula dengan:

    Format data yang diminta oleh hos untuk dibawa oleh tuan rumah apabila meminta pemalam protokol

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

    Struktur nilai balik yang akhirnya dihantar kepada pentadbir:

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

    Sebagai contoh, panggilan strategi:

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

    Kod ujian dalam plugin (dalam bahasa go):

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

    Pada baris perintah: 2017/08/31 10:19:59 Running http://127.0.0.1:6666/DigitalAsset

    Pencetak request.Method,request.Params dalam baris arahan plugin Permintaan yang dihantar oleh pentadbir dalam data yang diuraikan dalam Badan Permintaan:request.MethodUntuk:__api_cancel_borrow Permintaan yang dihantar oleh pentadbir dalam data yang diuraikan dalam Badan Permintaan:request.ParamsUntuk:{"borrow_id" : "123", "symbol" : "cny"}

    Customizable untuk menguruskan pertukaran akses langsung iniAPIdaripadaexchange.IOPanggilan.

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

    Pemprosesan automatik bawahexchange.GetRawJSONPerisian ini tidak memerlukan plugin untuk digunakan.

  • Sokongan untuk Exchange.Go

    Pemprosesan automatik bawahexchange.GoTidak perlu diuruskan dalam 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)
    

    Protokol universal platform dagangan kuantitatif pencipta disambungkan kepada pertukaran tersuai

    Protokol universal platform dagangan kuantitatif pencipta disambungkan kepada pertukaran tersuai

    # 注意:使用exchange.Go在wait的时候如果指定了超时时间, 
    #      一定要确保获取到最终的数据,这样申请的并发线程才能回收。
    
  • Sokongan untuk fungsi jangka masa

    Pemprosesan khusus yang diperlukan dalam program tambahan, seperti menetapkan tuas, kod kontrak, arah pesanan, boleh menetapkan rekod pembolehubah tempatan, untuk mendapatkan pegangan memerlukan akses ke antara muka bursa untuk mendapatkan data mentah dan memproses struktur pegangan yang ditakrifkan di platform FMZ dan kembali. Apabila fungsi berikut dipanggil dalam strategi, program plugin menerimaRpcFormat permintaan sedikit berbeza dengan antara muka lain, perlu diperhatikan bahawaRpcRequestFormat, perbezaan utama ialah nilai param adalah struktur komposit.

    • SetContractType
      Tetapkan kod kontrak.
      {"access_key":"123","method":"io","nonce":1623307269528738000,"params":{"args":["quarter"],"code":2},"secret_key":"123"}
    
    • SetDirection Tetapkan masa depan ke arah yang sama.
      {"access_key":"123","method":"io","nonce":1623308734966484000,"params":{"args":["closesell"],"code":1},"secret_key":"123"}
    
    • SetMarginLevel Menetapkan Leverage Futures
      {"access_key":"123","method":"io","nonce":1623308734966939000,"params":{"args":[12],"code":0},"secret_key":"123"}
    
    • GetPosition Mendapatkan pegangan berjangka. Apabilaexchange.GetPosition()Apabila dipanggil:
      {"access_key":"123","method":"io","nonce":1623308734967442000,"params":{"args":[],"code":3},"secret_key":"123"}
    

    Apabilaexchange.GetPosition("swap")Apabila dipanggil:

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

  • ### Contoh bahasa Go yang lengkap dengan plugin protokol umum ([Access to BitShares])
/*
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)
}