Type/to search
8
Follow
1364
Followers
Exemple d'accès au contrat d'accord général d'inventeur
Discussions
Created 2021-09-14 15:00:01  Updated 2024-12-04 21:17:44
 2
 2179

img

Exemple d'accès au contrat d'accord général d'inventeur

Récemment, certains utilisateurs ont parlé deAOFEXCet échange, considérant qu'il n'y a aucun exemple sur FMZ sur la façon de se connecter à l'interface REST du contrat Exchange. Cet article se concentrera sur l'accèsAOFEXPrenez ceci comme exemple pour expliquer comment accéder au contrat Exchange.

Il existe une distinction entre les bourses au comptant et les bourses à terme sur FMZ. Par exemple, les trois principales bourses que nous connaissons proposent à la fois des opérations au comptant et des opérations sur contrats. Ces différentes interfaces API de marché sont également différentes, et certaines ont même des systèmes API complètement séparés et indépendants. Par conséquent, lors du regroupement de ces échanges sur FMZ, une distinction est faite entre le spot et les contrats à terme.

Il existe déjà de nombreux exemples d'utilisation du protocole général de FMZ pour encapsuler des échanges spot dans la communauté et les documents de la plateforme FMZ, mais il n'existe pas d'exemple complet d'encapsulation d'un échange de contrat. Cependant, le programme de plug-in de protocole général de la version à terme packagée est fondamentalement le même que la version spot, sauf qu'il dispose de quelques interfaces supplémentaires.

Document du protocole général de la FMZ : https://www.fmz.com/bbs-topic/1052
Le document est accompagné d'un accès au protocole général d'échange spot DEMO

Les interfaces qui repèrent les objets d'échange et les objets d'échange à terme doivent encapsuler

L'interface principale de l'objet d'échange spot

  • exchange.GetTicker()
    Obtenez des données sur le marché des ticks pour les marchés au comptant et à terme.

  • exchange.GetDepth()
    Obtenez les données du carnet de commandes pour les marchés au comptant et à terme.

  • exchange.GetTrades()
    Obtenir des données sur les flux d'ordres (enregistrements des transactions sur le marché), tant pour les marchés au comptant que pour les marchés à terme.

  • exchange.GetRecords()
    Pour obtenir des données K-line, des données au comptant et à terme sont nécessaires.

  • exchange.GetAccount()
    Obtenez des données sur les actifs du compte pour les marchés au comptant et à terme.

  • exchange.Buy()
    Pour passer un ordre d'achat, des ordres au comptant et à terme sont nécessaires.

  • exchange.Sell()
    Pour passer un ordre de vente, un ordre au comptant et à terme sont nécessaires.

  • exchange.GetOrder()
    Obtenez les données de commande de l'ID spécifié, au comptant et à terme.

  • exchange.GetOrders()
    Obtenez les commandes en attente actuellement actives, au comptant et à terme.

  • exchange.CancelOrder()
    Annulez la commande avec l'ID spécifié, à la fois pour le spot et les contrats à terme.

En plus d'encapsuler ces interfaces d'objets d'échange au comptant, les objets d'échange à terme doivent également encapsuler les fonctions d'interface utilisées par les contrats à terme.

  • exchange.SetMarginLevel()
    Définissez la valeur de l’effet de levier pour le symbole actuel.

  • exchange.SetDirection()
    Définissez la direction de négociation du produit actuel, c'est-à-dire ouvrir une position longue/ouvrir une position courte/fermer une position longue/fermer une position courte.

  • exchange.SetContractType()
    Définissez le code du contrat actuel. Parce que les contrats à terme ont des contrats perpétuels (swap)、Contrat de livraison(quarterTrimestriellement, etc., chacun a son propre code de contrat défini sur FMZ. Pour plus de détails, veuillez vous référer au document API FMZ. Ces paramètres doivent également être respectés lors de l'encapsulation, sinon les anciennes stratégies existantes risquent de ne pas fonctionner correctement.

  • exchange.GetPosition()
    Obtenez les données de position actuelles. Ici, nous pouvons voir qu'il n'existe pas de notion de position dans le trading au comptant. La position logique ne peut être calculée qu'en comparant les variations de compte. Mais les contrats à terme ont des positions.

Les données du corps de la requête envoyée par le dépositaire au plug-in du protocole universel lors de l'appel de l'interface spécifique aux contrats à terme dans la stratégie

En prenant l'exemple de l'échange AOFEX, si vous configurez l'objet d'échange du protocole général sur FMZ, la CLÉ API renseignée est :

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

img

Sur la page de configuration générale du protocole FMZ, il y aaccessKeyetsecretKeyZone de saisie, après avoir configuré l'objet d'échange de protocole universel, les données au format JSON du corps de la requête reçue lorsque le plug-in de protocole universel est en cours d'exécutionaccessKeyLa valeur du champ est212f54a1-1c88-1bf5-54a1-f7bf52b3256csecret_keyLa valeur du champ est7RJPKpBJMBkUL87RJPKpkULJPSUpsaKpUL83ysDs

Lors de l'appel de l'interface suivante, l'hôte émettra une requête RPC au plug-in de protocole général comme suit :

  • Lorsque la politique est appeléeexchange.SetMarginLevel(10)Lorsque les données dans le corps de la requête sont :

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

    Appelez exchange.SetMarginLevel(10) avec 10 comme paramètre.

  • Lorsque la politique est appeléeexchange.SetDirection("buy")Lorsque les données dans le corps de la requête sont :

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

    Appelez exchange.SetDirection("buy") et transmettez "buy" comme paramètre.

  • Lorsque la politique est appeléeexchange.SetContractType("swap")Lorsque les données dans le corps de la requête sont :

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

    Appelez exchange.SetContractType("swap") et transmettez « swap » comme paramètre.

  • Lorsque la politique est appeléeexchange.GetPosition()Lorsque les données dans le corps de la requête sont :

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

    Lors de l'appel de exchange.GetPosition(), aucun paramètre n'est passé.

Observez les données JSON dans le corps de la requête ci-dessus et constatez que :

  • Lorsqu'une fonction spécifique à un objet d'échange à terme est appelée, le corps de la requêtemethodLes valeurs du champ sontio
  • Distinguer ces fonctions nécessiteparamsDans le champcodeLe jugement, c'est-à-dire :
    • codepour0OuiSetMarginLevel
    • codepour1OuiSetDirection
    • codepour2OuiSetContractType
    • codepour3OuiGetPosition
  • Lorsque ces fonctions spécifiques à l'objet d'échange de contrats à terme sont appelées, les paramètres transmis se trouvent dans le corps de la requête.paramsChampargsmilieu.

Par rapport à l'exemple de plug-in de langage Go existant, vous devezOnPostFaire une extension dans la fonction :
Voir le code suivant pour "Fonctions nécessaires pour étendre les objets d'échange à terme"L'emplacement du commentaire.

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/SetContractTypeD'après le sens littéral, nous pouvons voir que les trois fonctions sont utilisées pour définir la configuration pertinente du produit commercial actuel. dansSetDirection/SetContractTypeD'un point de vue de conception, une variable locale est définie pour enregistrer la direction de la commande actuelle (c'est-à-dire que lors de la passation d'une commande, ce paramètre doit être lu pour savoir dans quelle direction la commande doit être passée. La raison en est qu'il existe deux directions pour achat à terme : ouverture à long terme et clôture à court terme. Il est donc nécessaire de faire la distinction entre le code du contrat en cours et le code du contrat en cours (quel contrat est interrogé lors de l'obtention d'informations sur le marché, de la passation d'ordres, etc.).

SetMarginLevelIl doit être conçu spécifiquement en fonction du mécanisme de levier de la bourse (1. Le paramètre de levier est transmis en tant que paramètre dans l'interface de commande. 2. La bourse dispose d'une interface pour définir l'effet de levier).

GetPositionIl s'agit d'une fonction permettant d'obtenir la position actuelle du produit. Lorsque le plug-in de protocole général obtient les données renvoyées par l'interface de position d'échange, il construit directement la fonction de position sur FMZ.positionLa même structure de données est suffisante.

Exemple complet de plug-in du protocole général des contrats à terme AOFEX :

Aller

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)