4
ध्यान केंद्रित करना
1271
समर्थक

आविष्कारक सामान्य समझौता अनुबंध पहुँच उदाहरण

में बनाया: 2021-09-14 15:00:01, को अपडेट: 2024-12-04 21:17:44
comments   2
hits   1802

आविष्कारक सामान्य समझौता अनुबंध पहुँच उदाहरण

आविष्कारक सामान्य समझौता अनुबंध पहुँच उदाहरण

हाल ही में, कुछ उपयोगकर्ता इस बारे में बात कर रहे हैंAOFEXयह एक्सचेंज, इस बात पर विचार करते हुए कि FMZ पर अनुबंध एक्सचेंज के REST इंटरफ़ेस से कनेक्ट करने के बारे में कोई उदाहरण नहीं है। यह लेख पहुंच पर केंद्रित होगाAOFEXअनुबंध एक्सचेंज तक पहुंचने के तरीके को समझाने के लिए इसे एक उदाहरण के रूप में लें।

एफएमजेड पर स्पॉट एक्सचेंजों और वायदा एक्सचेंजों के बीच अंतर है। उदाहरण के लिए, जिन तीन प्रमुख एक्सचेंजों से हम परिचित हैं, उनमें स्पॉट ट्रेडिंग और कॉन्ट्रैक्ट ट्रेडिंग दोनों होती हैं। ये विभिन्न बाजार API इंटरफेस भी भिन्न हैं, और कुछ में तो पूरी तरह से अलग और स्वतंत्र API प्रणालियां भी हैं। इसलिए, जब इन एक्सचेंजों को एफएमजेड पर पैकेजिंग किया जाता है, तो स्पॉट और फ्यूचर्स के बीच अंतर किया जाता है।

FMZ प्लेटफॉर्म समुदाय और दस्तावेजों में स्पॉट एक्सचेंजों को समाहित करने के लिए FMZ के सामान्य प्रोटोकॉल का उपयोग करने के कई उदाहरण पहले से ही मौजूद हैं, लेकिन अनुबंध एक्सचेंज को समाहित करने का कोई पूर्ण उदाहरण नहीं है। हालाँकि, पैकेज्ड फ्यूचर्स संस्करण का सामान्य प्रोटोकॉल प्लग-इन प्रोग्राम मूलतः स्पॉट संस्करण के समान ही है, सिवाय इसके कि इसमें कुछ अधिक इंटरफेस हैं।

FMZ सामान्य प्रोटोकॉल दस्तावेज़: https://www.fmz.com/bbs-topic/1052 यह दस्तावेज़ स्पॉट एक्सचेंज सामान्य प्रोटोकॉल एक्सेस डेमो के साथ आता है

एक्सचेंज ऑब्जेक्ट्स और फ्यूचर्स एक्सचेंज ऑब्जेक्ट्स को स्पॉट करने वाले इंटरफेस को एनकैप्सुलेट करने की आवश्यकता होती है

स्पॉट एक्सचेंज ऑब्जेक्ट का मुख्य इंटरफ़ेस

  • exchange.GetTicker() स्पॉट और वायदा दोनों के लिए टिक बाजार डेटा प्राप्त करें।

  • exchange.GetDepth() स्पॉट और फ्यूचर्स दोनों के लिए ऑर्डर बुक डेटा प्राप्त करें।

  • exchange.GetTrades() स्पॉट और फ्यूचर्स दोनों के लिए ऑर्डर फ्लो डेटा (बाजार लेनदेन रिकॉर्ड) प्राप्त करें।

  • exchange.GetRecords() के-लाइन डेटा प्राप्त करने के लिए स्पॉट और फ्यूचर्स दोनों की आवश्यकता होती है।

  • exchange.GetAccount() स्पॉट और वायदा दोनों के लिए खाता परिसंपत्ति डेटा प्राप्त करें।

  • exchange.Buy() खरीद ऑर्डर देने के लिए स्पॉट और फ्यूचर दोनों की आवश्यकता होती है।

  • exchange.Sell() विक्रय आदेश देने के लिए स्पॉट और वायदा दोनों की आवश्यकता होती है।

  • exchange.GetOrder() निर्दिष्ट आईडी का ऑर्डर डेटा प्राप्त करें, स्पॉट और फ्यूचर दोनों।

  • exchange.GetOrders() वर्तमान में सक्रिय लंबित ऑर्डर, स्पॉट और वायदा दोनों प्राप्त करें।

  • exchange.CancelOrder() स्पॉट और फ्यूचर्स दोनों के लिए निर्दिष्ट आईडी के साथ ऑर्डर रद्द करें।

स्पॉट एक्सचेंज ऑब्जेक्ट्स के इन इंटरफेस को समाहित करने के अलावा, फ्यूचर्स एक्सचेंज ऑब्जेक्ट्स को फ्यूचर्स द्वारा प्रयुक्त इंटरफेस फंक्शन्स को भी समाहित करने की आवश्यकता होती है।

  • exchange.SetMarginLevel() वर्तमान प्रतीक के लिए उत्तोलन मान निर्धारित करें.

  • exchange.SetDirection() वर्तमान उत्पाद की ट्रेडिंग दिशा निर्धारित करें, अर्थात, लंबी स्थिति खोलें/छोटी स्थिति खोलें/लंबी स्थिति बंद करें/छोटी स्थिति बंद करें।

  • exchange.SetContractType() वर्तमान अनुबंध कोड सेट करें. क्योंकि वायदा में स्थायी अनुबंध होते हैं (swap)、डिलीवरी अनुबंध(quarterत्रैमासिक), आदि, प्रत्येक का FMZ पर अपना स्वयं का अनुबंध कोड परिभाषित है। विवरण के लिए, कृपया FMZ API दस्तावेज़ देखें। इन सेटिंग्स का पालन एनकैप्सुलेट करते समय भी किया जाना आवश्यक है, अन्यथा मौजूदा पुरानी रणनीतियाँ ठीक से काम नहीं कर सकती हैं।

  • exchange.GetPosition() वर्तमान स्थिति डेटा प्राप्त करें. यहाँ हम देख सकते हैं कि स्पॉट ट्रेडिंग में पोजीशन की कोई अवधारणा नहीं है। तार्किक पोजीशन की गणना केवल अकाउंट में होने वाले बदलावों की तुलना करके ही की जा सकती है। लेकिन वायदा में स्थितियां होती हैं।

रणनीति में फ्यूचर्स-विशिष्ट इंटरफ़ेस को कॉल करते समय कस्टोडियन द्वारा यूनिवर्सल प्रोटोकॉल प्लग-इन को भेजे गए अनुरोध में बॉडी डेटा

AOFEX एक्सचेंज को उदाहरण के रूप में लेते हुए, यदि आप FMZ पर सामान्य प्रोटोकॉल के एक्सचेंज ऑब्जेक्ट को कॉन्फ़िगर करते हैं, तो भरी गई API KEY है:

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

आविष्कारक सामान्य समझौता अनुबंध पहुँच उदाहरण

FMZ सामान्य प्रोटोकॉल कॉन्फ़िगरेशन पृष्ठ पर हैaccessKeyऔरsecretKeyइनपुट बॉक्स, यूनिवर्सल प्रोटोकॉल एक्सचेंज ऑब्जेक्ट को कॉन्फ़िगर करने के बाद, यूनिवर्सल प्रोटोकॉल प्लग-इन चलने पर प्राप्त अनुरोध में बॉडी का 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मध्य।

मौजूदा गो भाषा प्लग-इन उदाहरण की तुलना में, आपको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शाब्दिक अर्थ से, हम देख सकते हैं कि तीन कार्यों का उपयोग वर्तमान व्यापारिक उत्पाद के प्रासंगिक कॉन्फ़िगरेशन को सेट करने के लिए किया जाता है। मेंSetDirection/SetContractTypeडिज़ाइन के नज़रिए से, एक स्थानीय चर को वर्तमान ऑर्डर दिशा रिकॉर्ड करने के लिए सेट किया जाता है (यानी, ऑर्डर देते समय, यह सेटिंग यह जानने के लिए पढ़ी जानी चाहिए कि ऑर्डर किस दिशा में रखा जाना है। इसका कारण यह है कि वायदा खरीद की दो दिशाएँ होती हैं : लॉन्ग ओपनिंग और शॉर्ट क्लोजिंग। इसलिए, वर्तमान अनुबंध कोड और वर्तमान अनुबंध कोड (बाजार की जानकारी प्राप्त करते समय, ऑर्डर देते समय, आदि किस अनुबंध के बारे में पूछताछ की जा रही है) के बीच अंतर करना आवश्यक है।

SetMarginLevelइसे विशेष रूप से एक्सचेंज के लीवरेज तंत्र के अनुसार डिजाइन किया जाना चाहिए (1. लीवरेज पैरामीटर को ऑर्डर इंटरफ़ेस में पैरामीटर के रूप में पास किया जाता है। 2. एक्सचेंज में लीवरेज सेट करने के लिए एक इंटरफ़ेस है)।

GetPositionयह उत्पाद की वर्तमान स्थिति प्राप्त करने के लिए एक फ़ंक्शन है। जब सामान्य प्रोटोकॉल प्लग-इन एक्सचेंज स्थिति इंटरफ़ेस द्वारा लौटाए गए डेटा को प्राप्त करता है, तो यह सीधे FMZ पर स्थिति फ़ंक्शन का निर्माण करता है।positionसमान डेटा संरचना पर्याप्त है।

पूर्ण AOFEX फ्यूचर्स सामान्य प्रोटोकॉल प्लग-इन उदाहरण:

जाना

”`go /* 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