Type/to search
8
Follow
1364
Followers
発明者の定量取引プラットフォームユニバーサルプロトコルはカスタム取引所に接続されている
Tutorials
Created 2017-08-24 16:29:56  Updated 2021-06-10 15:33:02
 23
 17884

ゲノムプロトコル使用文書

このGPRSを利用して,API取引の取引所,特定のAPI協定は,無制限ですrestwebsocketfixオンラインで利用できる
Pythonの通用プロトコル例:https://www.fmz.com/strategy/101399

  • 1 ,GPLプラグインの実行,ポート設定

    監視アドレスの設定とポートの設定を記述する.
    例:http://127.0.0.1:6666/DigitalAsset選択してください.http://127.0.0.1:6666/exchange

    なぜこのような設定をするのか?IP経路についてウールの布?
    それは,発明者による量化ページコントロールセンター飛び回る取引所を追加するページは,GPRSタグを選択し,除いて表示しますAPI-KEYサービスアドレスの<unk>がいて,このサービスアドレスは,ホストがアクセスすべきIPとポートを教えてくれます (ホストとGPLプラグインは同じデバイスで動作しません). サービスアドレスの例を入力します.http://127.0.0.1:6666/DigitalAssetDigitalAsset名前で表すのは,自分の定義の名前です.

    発明者の量化追加取引所のページはこんな感じです.
    通常,取引所の配置アカウントの情報は,配置のみが必要となります.access keyそしてsecret keyしかし,いくつかの取引所のAPIインターフェースは取引パスワードの送信を要求する (例えば,いくつかの取引所の注文インターフェース),この場合,GPRSページに余分なコントロールがないので,これらの情報を書き込む.このような取引所のAPIに出会ったとき,我々は余分な転送が必要な設定情報を書き込むことができます.secret keyメールの内容が 敏感でない場合はaccess key文字列を入力すると,split操作は,図の例のように,これらのデータを分離します.

    img

    プラグインで処理すると,XXX_PassWord
    この記事の最後の完全な例では, newBitgo の関数は:

    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

    グローバルGPLプラグインmain関数の例:
    Go言語説明:

    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) }
  • 2 応答関数

    指定されたポートを停止して監視するGPのプラグインを停止し,要求を送信する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 有介绍说明。 } */

    だから,GPRSのプラグインで受信されたリクエストボディデータから JSONの逆序列化後に得られた構造体requestMethodコンピュータの操作はswitch異なる発明者の量化に関するAPIを分類する (つまり,ホスト上で実行されるポリシーを識別する) は,どの発明者が量化されているかを呼び出すAPI呼び出し:

    Go言語の例として:

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

    これらの分支は,返されたデータを,GPRのプラグインプログラムが応答する構造に書き込み,托管者の要求に応える.

    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 API コールの種類

    言語は2つのカテゴリーに分類されます.

    1. 署名を必要としないパブリック・インターフェース:

    GetTicker()

    GetDepth()

    GetTrades()

    GetRecords(period)
    ...

    1. 署名を必要とするユーザーインターフェース,例えば:

    BuySell

    GetOrder(id)

    GetOrders()

    GetAccount()

    CancelOrder(id)
    ...
    各取引所の署名形式は異なるため,必要に応じて具体的に作成する必要があります.

  • 4 発明者はAPIの呼び出しを量化するGPL プラグインそしてホストインタラクティブなデータ形式:

    発明者の数値化されたAPIのインターフェースは,GetName()GetLabel()呼び出されても,GPL プラグイン要求を送信する
    exchange.GetName()共通プラグインが設定されている取引所が呼び出されると"Exchange"に戻ります.

    • 1、GetTicker: オンラインの検索サイトは,

      ホスト応答の関数への送信requestmethod理由として:ticker

      管理者が送信したパラメータ:request.Params.symbol: 管理者がロボットのページで設定した通貨で送付する.

      ホストがGPLプラグインにリクエストする際の主体の持ち込みデータ形式 (JSON)

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

      最終的に托管者に送られる返り値構造: ((つまり,GPRSプラグインが取引所インターフェースを要請した後のデータが托管者に返される形式)

      JSON構造

      { "data": { "time": 1500793319499, // 毫秒时间戳,整型 "buy": 1000, // 以下浮点型 "sell": 1001, "last": 1005, "high": 1100, "low": 980, "vol": 523, } }
    • 2、GetRecords:取引所から提供されるK線データを取得する.

      管理者は,監視応答の関数に送信requestmethod理由として:records

      管理者が送信したパラメータ:request.Params.period関連値:exchange.GetRecordsこの関数の最初の参数,実際にrequest.Params.period周期は分数で表される.例えば日周期は60*24今すぐ1440request.Params.symbol: 管理者が設定した通貨で送信する.

      GMCP プラグインへの要求時にホストが主体に要求するデータ形式

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

      管理者に送られた最終回帰値の構造:

      JSON構造

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

      Go言語のテストデータ:

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

      発明者による量化プラットフォームLog見せるrecordsデータ:

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

      2次元数列の最初の要素はintタイプは,時間<unk>。2、管理者は自動的に時間を<unk>○○○○○○○○○○○○○○○○○○○○○○○○○○○○○○○○○○○○○○○○○○○○○○○○○○○○○○○○○○○○○○○○○○○○○○○○○○○○○○○○○○○○○○○○○○○○○○○○○○○○○○○○○○○○○○○○○○○○○○○○○○○○○○○○○○○○○○○○○○○○○○○○○○○○○○○○○○○○○○○○○○○○○○○○○○○○○○○○○○○○○○○○○○○○○○○○○○○○○○○○○○○○○○○○○○○○○○○○○○○○○○○○○○○○○○○○○○○○○○○○○

    • 3、GetDepth:取引所の詳細情報 (注文は薄い,売る 1 売る 2 買う 1 買う 2...)

      管理者は,監視応答の関数に送信requestmethod理由として:depth

      管理者が送信したパラメータ:request.Params.symbol: 管理者が設定したポリシーで送られるコイン

      GMCP プラグインへの要求時にホストが主体に要求するデータ形式

      { "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:取引所が提供している,特定の時間の取引記録 (自分の取引記録ではない) を取得

      管理者は,監視応答の関数に送信requestmethod理由として:trades

      管理者が送信したパラメータ:request.Params.symbol取引価値のある通貨は,例えば:btc管理者が策略で設定した通貨で送付されます.

      GMCP プラグインへの要求時にホストが主体に要求するデータ形式

      { "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:アカウントの資産情報

      管理者は,監視応答の関数に送信requestmethod理由として:accounts

      管理者が送信するパラメータ: ((注意! 通常はアカウントのすべての資産を取得する!, 取引所のインターフェースに応じて,単独で取得するか,総資産情報を取得する)

      GMCP プラグインへの要求時にホストが主体に要求するデータ形式

      { "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、Buy、Sell:委託書を送信し,取引を注文する.

      管理者は,監視応答の関数に送信requestmethod理由として:trade

      管理者が送信したパラメータ:request.Params.type管理人: 呼び出しによるexchange.Buyそしてexchange.Sellメールを送信request.Params.price戦略で呼び出されます.APIこの関数の最初の参数,request.Params.amount戦略で呼び出されます.APIこの関数の2番目の関数は,request.Params.symbol: 管理者が設定した通貨で送信する.

      GMCP プラグインへの要求時にホストが主体に要求するデータ形式

      { "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:指定された注文番号の注文情報を取得する

      管理者は,監視応答の関数に送信requestmethod理由として:order

      管理者が送信したパラメータ:request.Params.idrequest.Params.symbol

      GMCP プラグインへの要求時にホストが主体に要求するデータ形式

      { "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

      GMCP プラグインへの要求時にホストが主体に要求するデータ形式

      { "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、CancelOrder: 指定された注文番号の注文委託をキャンセルする

      管理者は,監視応答の関数に送信requestmethod理由として:cancel

      管理者が送信したパラメータ:request.Params.id: 文字列型,策略が呼び出すAPI関数の最初の参数,request.Params.symbol:btc (例) 管理者が策定した通貨で送付

      GMCP プラグインへの要求時にホストが主体に要求するデータ形式

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

      管理者に返される最終値の構造は:

      JSON構造

      { "data": true, // true or false }
    • 10、IO: 発明者の量化プラットフォームのexchange.IO関数を呼び出す

      管理者は,監視応答の関数に送信requestmethod理由として__api_スタートメソッドの名前

      GMCP プラグインへの要求時にホストが主体に要求するデータ形式

      { "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言語):

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

      プラグインのコマンド:
      2017/08/31 10:19:59 Running http://127.0.0.1:6666/DigitalAsset ...

      プラグインのコマンドラインで印刷されたrequest.Method,request.Params
      ホストが送信したリクエストボディのデータ解析後のリクエスト:request.Method理由として:__api_cancel_borrow
      ホストが送信したリクエストボディのデータ解析後のリクエスト:request.Params理由として:{"borrow_id" : "123", "symbol" : "cny"}

      取引所への直接アクセスをカスタマイズAPIexchange.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.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要求形式は他のインターフェースと少し異なるので,GPLのプラグインプログラムに注意してください.RpcRequestこの形式は,paramsの値が複合構造であることに違いがある.

      • SetContractType
        契約コードを設定する

        {"access_key":"123","method":"io","nonce":1623307269528738000,"params":{"args":["quarter"],"code":2},"secret_key":"123"}
      • SetDirection
        期貨の方向を設定する.

        {"access_key":"123","method":"io","nonce":1623308734966484000,"params":{"args":["closesell"],"code":1},"secret_key":"123"}
      • SetMarginLevel
        期貨のレバレッジを設定する

        {"access_key":"123","method":"io","nonce":1623308734966939000,"params":{"args":[12],"code":0},"secret_key":"123"}
      • GetPosition
        期貨保有権を取得する.
        したexchange.GetPosition()呼び出しの時間:

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

        したexchange.GetPosition("swap")呼び出しの時間:

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

  • 通用プロトコルプラグインの完全なGo言語の例 ((Access to BitShop Exchange))

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)