Type/to search
8
Follow
1364
Followers
発明者一般契約契約アクセス例
Discussions
Created 2021-09-14 15:00:01  Updated 2024-12-04 21:17:44
 2
 2179

img

発明者一般契約契約アクセス例

最近、一部のユーザーから次のような話が出ていますAOFEXこの Exchange では、契約 Exchange の REST インターフェイスに接続する方法についての例が FMZ にないことを考慮します。この記事ではアクセスに焦点を当てますAOFEXこれを例にして、契約取引所にアクセスする方法を説明します。

FMZ では、スポット取引所と先物取引所が区別されています。たとえば、私たちがよく知っている 3 つの主要取引所では、スポット取引と契約取引の両方が行われます。これらの異なる市場の API インターフェースも異なり、完全に別個で独立した API システムを持つものもあります。したがって、FMZ でこれらの取引所をカプセル化する場合、スポットと先物が区別されます。

FMZ プラットフォーム コミュニティやドキュメントには、FMZ の一般的なプロトコルを使用してスポット交換をカプセル化する例がすでに多数ありますが、契約交換をカプセル化する完全な例はありません。ただし、パッケージ化された先物バージョンの一般的なプロトコル プラグイン プログラムは、いくつかのインターフェイスが追加されていることを除いて、スポット バージョンと基本的に同じです。

FMZ 一般プロトコル文書: https://www.fmz.com/bbs-topic/1052
このドキュメントには、スポット取引所の一般的なプロトコルアクセスのデモが付属しています。

スポット交換オブジェクトと先物交換オブジェクトをカプセル化する必要があるインターフェース

スポット取引オブジェクトのメインインターフェース

  • exchange.GetTicker()
    スポットと先物両方のティック市場データを取得します。

  • exchange.GetDepth()
    スポットと先物両方の注文書データを取得します。

  • exchange.GetTrades()
    スポットと先物両方の注文フローデータ(市場取引記録)を取得します。

  • exchange.GetRecords()
    K ライン データを取得するには、スポットと先物の両方が必要です。

  • exchange.GetAccount()
    スポットと先物両方の口座資産データを取得します。

  • exchange.Buy()
    買い注文を出すには、スポットと先物の両方が必要です。

  • exchange.Sell()
    売り注文を出すには、スポットと先物の両方が必要です。

  • exchange.GetOrder()
    指定された ID のスポットと先物両方の注文データを取得します。

  • exchange.GetOrders()
    現在アクティブな保留中の注文(スポットと先物の両方)を取得します。

  • exchange.CancelOrder()
    スポットと先物の両方について、指定された ID の注文をキャンセルします。

スポット取引オブジェクトのこれらのインターフェースをカプセル化することに加えて、先物取引オブジェクトは先物によって使用されるインターフェース関数もカプセル化する必要があります。

  • exchange.SetMarginLevel()
    現在のシンボルのレバレッジ値を設定します。

  • exchange.SetDirection()
    現在の商品の取引方向(ロングポジションを開く/ショートポジションを開く/ロングポジションをクローズする/ショートポジションをクローズする)を設定します。

  • exchange.SetContractType()
    現在の契約コードを設定します。先物は永久契約なので(swap)、配送契約(quarter定期契約(四半期ごと)など、それぞれFMZ上で契約コードが定義されています。詳細はFMZ APIドキュメントをご参照ください。カプセル化するときにもこれらの設定に従う必要があります。そうしないと、既存の古い戦略が適切に機能しない可能性があります。

  • exchange.GetPosition()
    現在の位置データを取得します。ここで、スポット取引にはポジションの概念がないことがわかります。論理的なポジションは、アカウントの変化を比較することによってのみ計算できます。しかし、先物にはポジションがあります。

戦略内の先物固有のインターフェースを呼び出すときに、カストディアンがユニバーサルプロトコルプラグインに送信するリクエストの本文データ

AOFEX 取引所を例にとると、FMZ で一般プロトコルの取引所オブジェクトを設定する場合、入力される API キーは次のようになります。

  • accessKey : 212f54a1-1c88-1bf5-54a1-f7bf52b3256c
  • secretKey : 7RJPKpBJMBkUL87RJPKpkULJPSUpsaKpUL83ysDs

img

FMZの一般プロトコル設定ページにはaccessKeyそしてsecretKey入力ボックス、一般プロトコル交換オブジェクトを設定した後、一般プロトコルプラグインの実行時に受信したリクエストのBodyのJSON形式データaccessKeyフィールドの値は212f54a1-1c88-1bf5-54a1-f7bf52b3256csecret_keyフィールドの値は7RJPKpBJMBkUL87RJPKpkULJPSUpsaKpUL83ysDs

次のインターフェイスを呼び出すと、ホストは次のように一般的なプロトコル プラグインに RPC 要求を発行します。

  • ポリシーが呼び出されるとexchange.SetMarginLevel(10)リクエスト本文のデータが次の場合:

    { "access_key":"212f54a1-1c88-1bf5-54a1-f7bf52b3256c", "method":"io", "nonce":1631858961289247000, "params":{"args":[10],"code":0}, "secret_key":"7RJPKpBJMBkUL87RJPKpkULJPSUpsaKpUL83ysDs" }

    パラメータとして10を指定してexchange.SetMarginLevel(10)を呼び出します。

  • ポリシーが呼び出されるとexchange.SetDirection("buy")リクエスト本文のデータが次の場合:

    { "access_key":"212f54a1-1c88-1bf5-54a1-f7bf52b3256c", "method":"io", "nonce":1631860438946922000, "params":{"args":["buy"],"code":1}, "secret_key":"7RJPKpBJMBkUL87RJPKpkULJPSUpsaKpUL83ysDs" }

    exchange.SetDirection("buy") を呼び出し、パラメータとして "buy" を渡します。

  • ポリシーが呼び出されるとexchange.SetContractType("swap")リクエスト本文のデータが次の場合:

    { "access_key":"212f54a1-1c88-1bf5-54a1-f7bf52b3256c", "method":"io", "nonce":1631860847525039000, "params":{"args":["swap"],"code":2}, "secret_key":"7RJPKpBJMBkUL87RJPKpkULJPSUpsaKpUL83ysDs" }

    exchange.SetContractType("swap") を呼び出し、パラメータとして "swap" を渡します。

  • ポリシーが呼び出されるとexchange.GetPosition()リクエスト本文のデータが次の場合:

    { "access_key":"212f54a1-1c88-1bf5-54a1-f7bf52b3256c", "method":"io", "nonce":1631860996119505000, "params":{"args":[],"code":3}, "secret_key":"7RJPKpBJMBkUL87RJPKpkULJPSUpsaKpUL83ysDs" }

    exchange.GetPosition() を呼び出すときに、パラメーターは渡されません。

上記のリクエスト本文の JSON データを観察し、次の点を確認します。

  • 先物取引所オブジェクト固有の関数が呼び出されると、リクエスト本体はmethodフィールド値はio
  • これらの機能を区別するにはparams現場でcode判断とは、
    • codeのために0はいSetMarginLevel
    • codeのために1はいSetDirection
    • codeのために2はいSetContractType
    • codeのために3はいGetPosition
  • これらの先物取引オブジェクト固有の関数が呼び出されると、渡されるパラメータはリクエスト本体に含まれます。params分野args真ん中。

既存のGo言語プラグインの例と比較すると、OnPost関数を拡張します:
先物取引オブジェクトを拡張するために必要な関数「コメントの場所です。

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 := newZG(request.AccessKey, request.SecretKey) var symbol string if _, ok := request.Params["symbol"]; ok { symbol = strings.ToUpper(request.Params["symbol"].(string)) } var data interface{} switch request.Method { case "ticker": data, err = e.GetTicker(symbol, "GET") case "depth": data, err = e.GetDepth(symbol, "GET") case "trades": data, err = e.GetTrades(symbol, "GET") case "records": data, err = e.GetRecords(toInt64(request.Params["period"]), symbol, "GET") case "accounts": data, err = e.GetAccount(symbol, "GET") case "trade": side := request.Params["type"].(string) if side == "buy" { side = "BUY" } else { side = "SELL" } price := toFloat(request.Params["price"]) amount := toFloat(request.Params["amount"]) data, err = e.Trade(side, price, amount, symbol, "POST") case "orders": data, err = e.GetOrders(symbol, "POST") case "order": data, err = e.GetOrder(toString(request.Params["id"]), symbol, "POST") case "cancel": data, err = e.CancelOrder(toString(request.Params["id"]), symbol, "POST") default: if strings.HasPrefix(request.Method, "__api_") { params := map[string]interface{}{} for k, v := range request.Params { params[k] = toString(v) } data, err = e.tapiCall(request.Method[6:], params, "GET") } else if request.Method == "io" { // 扩展期货交易所对象需要的函数 code := toString(request.Params["code"]) if code == "0" { // 处理SetMarginLevel // ... } else if code == "1" { // 处理SetDirection // ... } else if code == "2" { // 处理SetContractType // ... } else if code == "3" { // 处理GetPosition // ... } else { panic(errors.New(request.Method + " not support")) } } else { panic(errors.New(request.Method + " not support")) } } if err != nil { panic(err) } ret = map[string]interface{}{ "data": data, } return }

SetMarginLevel/SetDirection/SetContractType文字通りの意味から、3 つの関数は現在の取引商品の関連する構成を設定するために使用されていることがわかります。でSetDirection/SetContractType設計の観点から、現在の注文方向を記録するためにローカル変数が設定されます(つまり、注文を出すときに、この設定を読み取って、注文がどの方向に出されるかを知る必要があります。その理由は、先物購入には2つの方向があるためです。 : ロングでオープンし、ショートでクローズします。そのため、現在の契約コードと現在の契約コード(マーケット情報取得、注文の発注などを行う際にどの契約を照会しているか)を区別する必要があります。

SetMarginLevel取引所のレバレッジメカニズムに合わせて特別に設計する必要があります(1. レバレッジパラメータは注文インターフェースのパラメータとして渡されます。2. 取引所にはレバレッジを設定するためのインターフェースがあります)。

GetPosition製品の現在位置を取得する機能です。汎用プロトコルプラグインは、交換位置インターフェースから返されたデータを取得すると、FMZ 上に位置関数を直接構築します。position同じデータ構造で十分です。

AOFEX 先物一般プロトコル プラグインの完全な例:

行く

mylang
/* CGO_ENABLED=0 GOOS=windows GOARCH=amd64 go build xxx.go */ package main import ( "bytes" "encoding/hex" "crypto/sha1" "encoding/json" "errors" "flag" "fmt" "io/ioutil" "log" "net/http" "net/url" "sort" "strconv" "strings" "time" // proxy "golang.org/x/net/proxy" "crypto/tls" "math/rand" ) var isUsedProxy bool = false var ct string = "" var direction string = "buy" var marginLevel float64 = 10 var currSymbol string = "" 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 } 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 } return ret } type headerTuple struct { name string value string } type Request struct { headers []headerTuple Proxy string Method string Uri string Body interface{} QueryString interface{} Timeout time.Duration ContentType string Accept string Host string UserAgent string } func (r *Request) AddHeader(name string, value string) { if r.headers == nil { r.headers = []headerTuple{} } r.headers = append(r.headers, headerTuple{name: name, value: value}) } type iAOFEX struct { accessKey string secretKey string clientID string currency string opCurrency string baseCurrency string quoteCurrency string apiBase string timeout time.Duration timeLocation *time.Location // ext contractTypes []string } 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].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 newiAOFEX(accessKey, secretKey string) *iAOFEX { s := new(iAOFEX) s.accessKey = accessKey s.secretKey = secretKey s.apiBase = "https://openapi-contract.aofex.info" s.timeout = 20 * time.Second s.timeLocation = time.FixedZone("Asia/Shanghai", 8*60*60) s.contractTypes = []string{"swap"} return s } func (p *iAOFEX) isValidContractType(contractType string) bool { for _, t := range p.contractTypes { if contractType == t { return true } } return false } func (p *iAOFEX) calcContractTypeMap(currency string) (contractTypeMap map[string]string, err error) { var baseCurrency, quoteCurrency string contractTypeMap = map[string]string{} if arr := strings.SplitN(currency, "_", 2); len(arr) == 2 { baseCurrency = arr[0] quoteCurrency = arr[1] } else { err = errors.New("symbol error!") return } contractTypeMap["swap"] = fmt.Sprintf("%s-%s", strings.ToUpper(baseCurrency), strings.ToUpper(quoteCurrency)) return } func (p *iAOFEX) apiCall(method string) (*Json, error) { req, err := http.NewRequest("GET", fmt.Sprintf("%s%s", p.apiBase, method), nil) if err != nil { return nil, err } fmt.Printf("\n %c[1;44;32m%s%c[0m\n", 0x1B, "apiCall GET create req:" + fmt.Sprintf("%s%s", p.apiBase, method), 0x1B) fmt.Println("req:", req) req.Header.Set("Content-Type", "application/json;utf-8") // proxy strProxy := "" client := http.DefaultClient if isUsedProxy { var auth *proxy.Auth proxyAddr := strings.Split(strProxy, "//")[1] if strings.Contains(proxyAddr, "@") { arr := strings.SplitN(proxyAddr, "@", 2) arrAuth := strings.SplitN(arr[0], ":", 2) proxyAddr = arr[1] auth = &proxy.Auth{} auth.User = arrAuth[0] if len(arrAuth) == 2 { auth.Password = arrAuth[1] } } var dialer proxy.Dialer if dialer, err = proxy.SOCKS5("tcp", proxyAddr, auth, proxy.Direct); err == nil { client = &http.Client{ Transport: &http.Transport{ Dial: dialer.Dial, MaxIdleConnsPerHost: 5, TLSClientConfig: &tls.Config{InsecureSkipVerify: true}, ResponseHeaderTimeout: 20 * time.Second, }, Timeout: 20 * time.Second, } } else { return nil, err } } resp, err := client.Do(req) if err != nil { return nil, err } defer resp.Body.Close() b, err := ioutil.ReadAll(resp.Body) if err != nil { return nil, err } var js *Json js, err = NewJson(b) if err != nil { return nil, err } // fault tolerant if _, ok := js.data.(map[string]interface{}); ok { if code, ok := js.MustMap()["code"]; ok { if toString(code) != "0" { err = errors.New(fmt.Sprintf("%v", js.data)) } } } return js, err } func (p *iAOFEX) GetTicker(symbol string) (ticker interface{}, err error) { mpCt, mpCtErr := p.calcContractTypeMap(symbol) if mpCtErr != nil { err = mpCtErr return } realCt, ok := mpCt[ct] if !ok { err = errors.New("invalid contractType!") return } var js *Json js, err = p.apiCall(fmt.Sprintf("/openApi/contract/market?symbol=%s", realCt)) if err != nil { return } depth, errDepth := p.GetDepth(symbol) if errDepth != nil { err = errDepth return } ask1 := depth.(map[string]interface{})["asks"].([][2]float64)[0][0] bid1 := depth.(map[string]interface{})["bids"].([][2]float64)[0][0] mp := js.Get("result").MustMap() ticker = map[string]interface{}{ "time": time.Now().UnixNano() / 1e6, "buy": toFloat(bid1), "sell": toFloat(ask1), "last": toFloat(mp["close"]), "high": toFloat(mp["high"]), "low": toFloat(mp["low"]), "vol": toFloat(mp["vol"]), } return } func (p *iAOFEX) GetDepth(symbol string) (depth interface{}, err error) { mpCt, mpCtErr := p.calcContractTypeMap(symbol) if mpCtErr != nil { err = mpCtErr return } realCt, ok := mpCt[ct] if !ok { err = errors.New("invalid contractType!") return } var js *Json js, err = p.apiCall(fmt.Sprintf("/openApi/contract/depth?symbol=%s", realCt)) if err != nil { return } asks := [][2]float64{} bids := [][2]float64{} for _, pair := range js.Get("result").Get("asks").MustArray() { arr := pair.([]interface{}) asks = append(asks, [2]float64{toFloat(arr[0]), toFloat(arr[1])}) } for _, pair := range js.Get("result").Get("bids").MustArray() { arr := pair.([]interface{}) bids = append(bids, [2]float64{toFloat(arr[0]), toFloat(arr[1])}) } depth = map[string]interface{}{ "time": js.Get("result").Get("ts").MustInt64(), "asks": asks, "bids": bids, } return } func (p *iAOFEX) GetTrades(symbol string) (trades interface{}, err error) { mpCt, mpCtErr := p.calcContractTypeMap(symbol) if mpCtErr != nil { err = mpCtErr return } realCt, ok := mpCt[ct] if !ok { err = errors.New("invalid contractType!") return } var js *Json js, err = p.apiCall(fmt.Sprintf("/openApi/contract/trade?symbol=%s", realCt)) if err != nil { return } items := []map[string]interface{}{} for _, pair := range js.Get("result").Get("data").MustArray() { item := map[string]interface{}{} mp := pair.(map[string]interface{}) item["id"] = toString(mp["id"]) item["price"] = toFloat(mp["price"]) item["amount"] = toFloat(mp["amount"]) item["time"] = toInt64(mp["ts"]) if toString(mp["direction"]) == "buy" { item["type"] = "buy" } else { item["type"] = "sell" } items = append(items, item) } for i := 0; i < len(items); i++ { for j := 0; j < len(items)-i-1; j++ { if toInt64(items[j]["time"]) > toInt64(items[j+1]["time"]) { items[j], items[j+1] = items[j+1], items[j] } } } trades = items return } func (p *iAOFEX) GetRecords(step int64, symbol string) (records interface{}, err error) { mpCt, mpCtErr := p.calcContractTypeMap(symbol) if mpCtErr != nil { err = mpCtErr return } realCt, ok := mpCt[ct] if !ok { err = errors.New("invalid contractType!") return } var periodDict map[int64]string = map[int64]string{ 1 : "1min", 5 : "5min", 15 : "15min", 30 : "30min", 60 : "1hour", 1440 : "1day", } period, okPeriod := periodDict[step] if !okPeriod { err = errors.New("period not support") return } var js *Json js, err = p.apiCall(fmt.Sprintf("/openApi/contract/kline?symbol=%s&period=%s&size=500", realCt, period)) if err != nil { return } items := []interface{}{} recordsData := js.Get("result").Get("data").MustArray() for i := len(recordsData) - 1 ; i >= 0 ; i-- { mp := recordsData[i].(map[string]interface{}) item := [6]interface{}{} item[0] = toInt64(mp["id"]) // time item[1] = toFloat(mp["open"]) // open item[2] = toFloat(mp["high"]) // high item[3] = toFloat(mp["low"]) // low item[4] = toFloat(mp["close"]) // close item[5] = toFloat(mp["vol"]) // vol items = append(items, item) } records = items return } func JSON_Encode(d interface{}) string { buffer := &bytes.Buffer{} encoder := json.NewEncoder(buffer) encoder.SetEscapeHTML(false) encoder.Encode(d) return buffer.String() } func (p *iAOFEX) tapiCall(httpMethod string, method string, params map[string]string) (js *Json, err error) { if params == nil { params = map[string]string{} } nonce := toString(time.Now().UnixNano() / 1e9) strLetter := "124567890abcdefghijklmnopqrstuvwxyz" for i := 0 ; i < 5 ; i++ { rand.Seed(time.Now().UnixNano()) nonce += string(strLetter[rand.Intn(len(strLetter))]) } arrParams := []string{} arrParams = append(arrParams, p.accessKey) arrParams = append(arrParams, p.secretKey) arrParams = append(arrParams, nonce) for k, v := range params { arrParams = append(arrParams, fmt.Sprintf("%s=%s", k, v)) } sort.Strings(arrParams) strSign := "" for _, ele := range arrParams { strSign += toString(ele) } h := sha1.New() h.Write([]byte(strSign)) signature := hex.EncodeToString(h.Sum(nil)) strUrl := fmt.Sprintf("%s%s", p.apiBase, method) if len(params) > 0 { strUrl = fmt.Sprintf("%s%s?%s", p.apiBase, method, encodeParams(params, false)) } req, err := http.NewRequest(httpMethod, strUrl, nil) if err != nil { return nil, err } req.Header.Add("Nonce", nonce) req.Header.Add("Token", p.accessKey) req.Header.Add("Signature", signature) strProxy := "" client := http.DefaultClient if isUsedProxy { var auth *proxy.Auth proxyAddr := strings.Split(strProxy, "//")[1] if strings.Contains(proxyAddr, "@") { arr := strings.SplitN(proxyAddr, "@", 2) arrAuth := strings.SplitN(arr[0], ":", 2) proxyAddr = arr[1] auth = &proxy.Auth{} auth.User = arrAuth[0] if len(arrAuth) == 2 { auth.Password = arrAuth[1] } } var dialer proxy.Dialer if dialer, err = proxy.SOCKS5("tcp", proxyAddr, auth, proxy.Direct); err == nil { client = &http.Client{ Transport: &http.Transport{ Dial: dialer.Dial, MaxIdleConnsPerHost: 5, TLSClientConfig: &tls.Config{InsecureSkipVerify: true}, ResponseHeaderTimeout: 20 * time.Second, }, Timeout: 20 * time.Second, } } else { return nil, err } } fmt.Printf("\n %c[1;44;32m%s%c[0m\n", 0x1B, "apiCall GET create req:" + fmt.Sprintf("%s%s", p.apiBase, method), 0x1B) fmt.Println("tapiCall req:", req) resp, err := client.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 err != nil { return nil, err } // fault tolerant if mp, ok := js.data.(map[string]interface{}); ok { if errno, ok := mp["errno"]; !ok || toString(errno) != "0" { err = errors.New(fmt.Sprintf("%v", js.data)) } } else { err = errors.New(fmt.Sprintf("%v", js.data)) } return js, err } func (p *iAOFEX) GetAccount(symbol string) (account interface{}, err error) { symbol = currSymbol mpCt, mpCtErr := p.calcContractTypeMap(symbol) if mpCtErr != nil { err = mpCtErr return } realCt, ok := mpCt[ct] if !ok { err = errors.New("invalid contractType!") return } var js *Json js, err = p.tapiCall("GET", "/openApi/contract/walletList", nil) if err != nil { return } assets := map[string]map[string]interface{}{} for _, ele := range js.Get("result").MustArray() { dic := ele.(map[string]interface{}) if realCt != toString(dic["symbol"]) { continue } arr := strings.SplitN(toString(dic["symbol"]), "-", 2) if arr[1] == "USDT" { if _, ok := assets[arr[1]]; !ok { assets[arr[1]] = map[string]interface{}{} } assets[arr[1]]["currency"] = arr[1] assets[arr[1]]["Info"] = dic assets[arr[1]]["free"] = toFloat(dic["avail"]) assets[arr[1]]["frozen"] = toFloat(dic["frozen"]) } } accounts := []map[string]interface{}{} for _, pair := range assets { accounts = append(accounts, pair) } account = accounts return } func (p *iAOFEX) Trade(side string, price, amount float64, symbol string) (orderId interface{}, err error) { mpCt, mpCtErr := p.calcContractTypeMap(symbol) if mpCtErr != nil { err = mpCtErr return } realCt, ok := mpCt[ct] if !ok { err = errors.New("invalid contractType!") return } params := map[string]string{} if direction == "buy" { params["contract_type"] = "open" } else if direction == "sell" { params["contract_type"] = "open" } else if direction == "closebuy" { params["contract_type"] = "close" } else if direction == "closesell" { params["contract_type"] = "close" } else { err = errors.New("invalid direction!") return } if (side == "buy-limit" || side == "buy-market") && (direction == "sell" || direction == "closebuy") { err = errors.New("invalid direction!") return } else if (side == "sell-limit" || side == "sell-market") && (direction == "buy" || direction == "closesell") { err = errors.New("invalid direction!") return } params["type"] = side params["lever_rate"] = toString(marginLevel) params["amount"] = toString(amount) params["symbol"] = realCt if price > 0 { params["price"] = toString(price) } var js *Json js, err = p.tapiCall("POST", "/openApi/contract/add", params) if err != nil { return } orderId = map[string]string{"id": toString(js.MustMap()["result"])} return } func (p *iAOFEX) GetOrders(symbol string) (orders interface{}, err error) { mpCt, mpCtErr := p.calcContractTypeMap(symbol) if mpCtErr != nil { err = mpCtErr return } realCt, ok := mpCt[ct] if !ok { err = errors.New("invalid contractType!") return } from := "" limit := 100 items := []map[string]interface{}{} for { params := map[string]string{ "symbol" : realCt, "limit" : toString(limit), } if from != "" { params["from"] = toString(from) } var js *Json js, err = p.tapiCall("GET", "/openApi/contract/currentList", params) if err != nil { return } arr := js.Get("result").MustArray() for _, ele := range arr { mp := ele.(map[string]interface{}) item := map[string]interface{}{} item["id"] = toString(mp["order_id"]) item["amount"] = toFloat(mp["amount"]) item["price"] = toFloat(mp["price"]) item["deal_amount"] = toFloat(mp["deal_amount"]) item["avg_price"] = toFloat(mp["price_avg"]) if toString(mp["type"]) == "buy-limit" || toString(mp["type"]) == "buy-market" || toString(mp["type"]) == "buy-tactics" || toString(mp["type"]) == "buy-market-tactic" || toString(mp["type"]) == "buy-plan" || toString(mp["type"]) == "buy-market-plan" { item["type"] = "buy" } else { item["type"] = "sell" } item["status"] = "open" item["contract_type"] = ct items = append(items, item) from = toString(mp["order_id"]) } if len(arr) < limit { break } } return items, nil } func (p *iAOFEX) GetOrder(orderId string, symbol string) (order interface{}, err error) { mpCt, mpCtErr := p.calcContractTypeMap(symbol) if mpCtErr != nil { err = mpCtErr return } realCt, ok := mpCt[ct] if !ok { err = errors.New("invalid contractType!") return } var js *Json js, err = p.tapiCall("GET", "/openApi/contract/historyList", map[string]string{ "from" : toString(orderId), "symbol" : realCt, "limit" : "1", }) if err != nil { return } item := map[string]interface{}{} for _, ele := range js.Get("result").MustArray() { mp := ele.(map[string]interface{}) if realCt != toString(mp["symbol"]) || toString(mp["order_id"]) != toString(orderId) { continue } item["id"] = toString(mp["order_id"]) item["amount"] = toFloat(mp["amount"]) item["price"] = toFloat(mp["price"]) item["deal_amount"] = toFloat(mp["deal_amount"]) item["avg_price"] = toFloat(mp["price_avg"]) item["contract_type"] = ct if toString(mp["type"]) == "buy-limit" || toString(mp["type"]) == "buy-market" || toString(mp["type"]) == "buy-tactics" || toString(mp["type"]) == "buy-market-tactic" || toString(mp["type"]) == "buy-plan" || toString(mp["type"]) == "buy-market-plan" { item["type"] = "buy" } else { item["type"] = "sell" } switch toString(mp["status"]) { case "1", "2": item["status"] = "open" case "3": item["status"] = "closed" case "4", "5", "6": item["status"] = "cancelled" } return item, nil } err = errors.New("order not found") return } func (p *iAOFEX) CancelOrder(orderId string, symbol string) (ret bool, err error) { mpCt, mpCtErr := p.calcContractTypeMap(symbol) if mpCtErr != nil { err = mpCtErr return } realCt, ok := mpCt[ct] if !ok { err = errors.New("invalid contractType!") return } _, err = p.tapiCall("POST", "/openApi/contract/cancel", map[string]string{ "order_ids" : toString(orderId), "symbol" : realCt, }) if err != nil { return } ret = true return } type RpcRequest struct { AccessKey string `json:"access_key"` SecretKey string `json:"secret_key"` Nonce int64 `json:"nonce"` Method string `json:"method"` Params map[string]interface{} `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 := newiAOFEX(request.AccessKey, request.SecretKey) symbol := strings.ToUpper(toString(request.Params["symbol"])) if _, ok := request.Params["symbol"]; ok { currSymbol = symbol } 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 := toString(request.Params["type"]) if side == "buy" { side = "buy-limit" if toFloat(request.Params["price"]) <= 0 { side = "buy-market" } } else { side = "sell-limit" if toFloat(request.Params["price"]) <= 0 { side = "sell-market" } } 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(toString(request.Params["id"]), symbol) case "cancel": data, err = e.CancelOrder(toString(request.Params["id"]), symbol) default: if strings.HasPrefix(request.Method, "__api_") { params := map[string]string{} for k, v := range request.Params { params[k] = toString(v) } data, err = e.tapiCall("GET", request.Method[6:], params) } else if request.Method == "io" { code := toString(request.Params["code"]) if code == "0" { if args, ok := request.Params["args"].([]interface{}); ok && len(args) == 1 { marginLevel = toFloat(args[0]) } else { err = errors.New(fmt.Sprintf("%v", request.Params)) } } else if code == "1" { if args, ok := request.Params["args"].([]interface{}); ok && len(args) == 1 { orderDirection := toString(args[0]) if orderDirection == "buy" || orderDirection == "sell" || orderDirection == "closebuy" || orderDirection == "closesell" { direction = orderDirection data = orderDirection } else { err = errors.New(fmt.Sprintf("not support orderDirection: %s", orderDirection)) } } else { err = errors.New(fmt.Sprintf("%v", request.Params)) } } else if code == "2" { if args, ok := request.Params["args"].([]interface{}); ok && len(args) == 1 { contractType := toString(args[0]) if e.isValidContractType(contractType) { ct = contractType data = contractType } else { err = errors.New(fmt.Sprintf("not support contractType: %s", contractType)) } } else { err = errors.New(fmt.Sprintf("%v", request.Params)) } } else if code == "3" { symbol := currSymbol mpCt, mpCtErr := e.calcContractTypeMap(symbol) if mpCtErr != nil { panic(mpCtErr) } realCt, ok := mpCt[ct] if !ok { err = errors.New("invalid contractType!") panic(err) } var js *Json js, err = e.tapiCall("GET", "/openApi/contract/position", map[string]string{ "symbol" : realCt, }) if err != nil { panic(err) } items := []map[string]interface{}{} for _, ele := range js.Get("result").MustArray() { mp := ele.(map[string]interface{}) item := map[string]interface{}{} item["MarginLevel"] = toFloat(mp["lever_rate"]) item["Amount"] = toFloat(mp["amount"]) item["FrozenAmount"] = toFloat(mp["contract_frozen"]) item["Price"] = toFloat(mp["open_price_avg"]) item["Profit"] = toFloat(mp["un_profit"]) if toString(mp["type"]) == "1" { item["Type"] = 0 } else { item["Type"] = 1 } item["ContractType"] = ct item["Margin"] = toFloat(mp["bood"]) items = append(items, item) } data = items } else { panic(errors.New(request.Method + " not support")) } } 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:6617", "bind addr") flag.Parse() if *addr == "" { flag.Usage() return } basePath := "/AOFEX" log.Println("Running ", fmt.Sprintf("http://%s%s", *addr, basePath), "...") http.HandleFunc(basePath, OnPost) http.ListenAndServe(*addr, nil) }
Related Recommendations
Comment
All comments (2)

    麻了,这也太难了

    4 years ago

    找个程序员,2天就帮你搞定了。

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