この通用プロトコルのいずれかにアクセスできます.
API
取引の取引所,特定のAPIプロトコルの制限はありません.rest
、websocket
、fix
メディアは,インターネットの利用者を対象としています. Pythonの一般的なプロトコルの例:https://www.fmz.com/strategy/101399
盗聴アドレスとポート設定を書き込みます.
例えば:http://127.0.0.1:6666/DigitalAsset
メディアは,http://127.0.0.1:6666/exchange
。
なぜこれを設定するのか?IP、経路ほら
原因は発明者の量化ページコントロールセンタージャンプエクスチェンジを追加ページをクリックすると, "General Protocol" を選択します.API-KEY
サービスアドレスのアドレスは,ホストがどのIPとポートにアクセスすべきかを伝える (ホストとUPRは同じデバイスで実行できない) サービスアドレスの例を記入します.http://127.0.0.1:6666/DigitalAsset
。DigitalAsset
この名前は自明な名前ですが,例として挙げます.
発明者による量化追加取引所のページは,以下のように見えます.
通常,取引所では,アカウントの設定情報を設定するだけです.access key
そしてsecret key
しかし,一部の取引所のAPIインターフェイスは取引パスワードを伝達することを要求する (例えば,一部の取引所の下記のインターフェイス),このとき,通用プロトコルページに余分なコントロールが書き込まれるので,そのような取引所のAPIに出くわしたとき,余分な伝達が必要な設定情報を書き込むことができます.secret key
メールの送信は,メールの送信先に表示されます.access key
文字列を書き換えるには,split
図示のように,このデータを分離する操作をします.
プラグインで処理し,それを取得します.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
}
汎用プロトコルプラグイン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)
}
一般プロトコルのプラグインを操作する パックプログラムは,指定されたポートを絶えず監視し,要求が送信されているか確認します.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のデータに対する反シリアル化によって得られる構造です.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:
...
これらのブランチは,実行後に返されるデータを,通用プロトコルプラグインが返答する構造に書き込み,托管者の要求に応答する.
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)) // 测试
}()
基本的には2つのカテゴリーに分かれます. 署名を必要としない公共インターフェース,例えば:
GetTicker()
GetDepth()
GetTrades()
GetRecords(period)
…
2 署名が必要なユーザーインターフェース,例えば:
Buy
、Sell
GetOrder(id)
GetOrders()
GetAccount()
CancelOrder(id)
ありがとうございました.
取引所によって署名方法が異なることもあり,必要に応じて書き込む必要がある.
発明者によって量化されたAPIは
GetName()
,GetLabel()
この関数は,等式関数で,一般プロトコルプラグインメールの送信は,exchange.GetName()
一般プラグイン設定の取引所への呼び出しでは"Exchange"が返されます.
管理者監視応答関数に送信します.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交易对举例
}
最終的に托管者に送信される返却値構造: (即ち,通用プロトコルプラグインが取引所のインターフェースを要求したデータが托管者に返されるフォーマット)
JSON構造
{
"data": {
"time": 1500793319499, // 毫秒时间戳,整型
"buy": 1000, // 以下浮点型
"sell": 1001,
"last": 1005,
"high": 1100,
"low": 980,
"vol": 523,
}
}
監視応答機能に送信します.request
中身method
理由:records
パラメータを送信する管理者:request.Params.period
価値関連exchange.GetRecords
この関数の最初のパラメータは,request.Params.period
周期は数分で表される.例えば,日周期は60*24
これは1440
、request.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],
...
]
}
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
管理者は自動的に 1000 を掛ける時間を掛ける.
監視応答機能に送信します.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], ... ],
}
}
監視応答機能に送信します.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",
},{
...
}
]
}
監視応答機能に送信します.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)
}
監視応答機能に送信します.request
中身method
理由:trade
パラメータを送信する管理者:request.Params.type
管理者:呼び出しによってexchange.Buy
ありがとうございましたexchange.Sell
投稿者:request.Params.price
戦略で呼び出す:API
この関数の最初のパラメータはrequest.Params.amount
戦略で呼び出す:API
この関数の2番目のパラメータは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也可以是字符串类型
}
}
監視応答機能に送信します.request
中身method
理由:order
パラメータを送信する管理者:request.Params.id
、request.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
}
}
監視応答機能に送信します.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"
},{
...
}]
}
監視応答機能に送信します.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
}
監視応答機能に送信します.request
中身method
ありがとうございました.__api_
方法の名前は,最初の文字で始まる.
管理者が通用プロトコルのプラグインに要求する際に主体によって運ばれるデータ形式
{
"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.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)
# 注意:使用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"}
/*
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)
}
小草パイトン接点プロトコルのカスタマイズされた取引所の例 https://www.fmz.com/strategy/101399
小草Python接点プロトコルのカスタマイズされた交換例https://www.fmz.com/strategy/101399
リウウイ9090bitmexの取引所はプラットフォームにアクセスしていますが, _C ((exchange.GetTicker).Buy とか _C ((exchange.GetDepth).Bids[0]はどちらも購入価格を得ることができず,エラーも返します.なぜかわかりません.
毎日 柔らかい模様ハーハ,GPLのプラグインを書けば,完全に交換APIを連結できるのか,それとも自分のプログラムで直接使うのか?
シュチ751ロボット/戦略/取引所の配置など,完全なプロトコルのアクセス例はありますか? 模倣以上の話,ハハハ
ほらこのコードは,UTPプラグインですよね? このプラグインは,私が直接呼び出すことができる策略模型のようなものではありませんか? 私はpy post またはgetを使って送信し,データを取得し,解析します. このUTPプラグインのコードは, botvsのAPIを受信し,それを解析し,監視アドレスとポートを設定する http://127.0.0.1:6666/DigitalAssetを書き,実際にはhttps://www.bitmex.com/のようなアドレスではありませんか?
シャリーPythonの場合は,これだけです.
ゼーディプラグインを使うことは,多くの関数を自分で書くことを意味するのか,例えばGetAccout (?
アランヤオbotVSはjsの例ではありませんか?なぜjsがないのか?なぜGOがあるのか?
nxtプレーヤー6号機が離陸したが,うまくいかない.
婚約者も野蛮な
発明者 量化 - 微かな夢BITMEXはフューチャー取引所である. 操作または取得する契約を設定するには,APIのドキュメント exchange.SetContractType関数を参照してください.
毎日 柔らかい模様そして,
発明者 量化 - 微かな夢しかし,もし戦略がマルチプラットフォームだったら? 森を1本の木のために捨てるわけにはいかない!
発明者 量化 - 微かな夢実行可能なファイルにコンパイルし,管理者と一緒に実行できます. この記事へのトラックバック一覧です. Python の例はまだないので,時間を取って作ってみました.
ほらプラグインは,それを実行させる方法,例えばpy形式で書き,ホストexeディレクトリに直接置くか?また,ローカルpyプログラムでプラグインのpyファイルディレクトリに参照のパスを追加するか?
発明者 量化 - 微かな夢ポリシーは特別な処理を必要とせず,BotVS apiのインターフェースでは,これらの取引所のオブジェクトは同じである (BotVSがアクセスされているか,またはあなたが自分で書いた通用プロトコルプラグインがサポートされているか),ポリシーは変更する必要はありません. このプラグインと管理者が一緒に動作します. 上記のドキュメントを参照してください: 1 汎用プロトコルプラグインを実行,ポート設定 この記事へのトラックバック一覧です.
ほらこの機能は,自分の書いた後,プラグインに対応する取引所がここに関数を呼び,他の取引所が既存のbotvs apiを使用する策略模型のようです.
発明者 量化 - 微かな夢- この汎用プロトコルプラグインのコードは, botvsのAPIを接続しながら,post、get を解読し,実行しているのですか?
発明者 量化 - 微かな夢Pythonで実装したものは同じです インターフェースやフォーマットも同じです 言語の違いだけです.
発明者 量化 - 微かな夢この例は,ホストの外部プラグイン形式で,BotVSプラットフォームへのアクセスのためのいくつかの取引所のインタラクティブコードを書き出すことを目的としています. PHP,node.js,python,Goなどの言語でこのようなプラグインを書き出すことができます.
発明者 量化 - 微かな夢はい,BotVS API GetAccountなどのインターフェース機能の具体的実装を書き出すことは,接続された取引所のコードを自分で書き込むことと同じです.
発明者 量化 - 微かな夢Python,node.js,PHP,Golangなども使用できます.