Die jüngsten BenutzerAOFEX
Dieser Exchange, da es auf FMZ noch kein Beispiel dafür gibt, wie man eine REST-Schnittstelle für den Exchange-Vertrag verknüpft.AOFEX
Zum Beispiel, wie man auf den Exchange-Vertrag zugreift.
Auf der FMZ gibt es eine Unterscheidung zwischen den Depot- und Futures-Börsen. Zum Beispiel unterscheiden sich die drei bekannten Märkte zwischen Depot- und Futures-Börsen. Die verschiedenen API-Schnittstellen dieser Märkte unterscheiden sich auch, und einige von ihnen sind sogar völlig unabhängig von ihren eigenen API-Systemen.
In der FMZ-Plattform-Community gibt es bereits viele Beispiele, aber es gibt noch keine vollständigen Beispiele für die Verwendung von FMZ-Prozeduren für die Verpackung von Exchange.
Das FMZ-Generalprotocol-Dokument:https://www.fmz.com/bbs-topic/1052Ein Dokument mit einem Exchange-Demonstrationsprotokoll für den Zugang zu einem Exchange-Demonstrationsprogramm
Hauptinterface für die Gegenstände der Bargeldbörse
exchange.GetTicker (() Der Markt für Ticks, Futures und Bargeld, ist ein wichtiger Faktor für die Entwicklung des Ticks.
exchange.GetDepth (,) ist ein Netzwerk, das sich mit der Erstellung von Die Daten über Auftragsdefizite und Terminkurse sind verfügbar.
exchange.GetTrades (WEB ist eine Online-Webseite, die sich mit der Entwicklung von Exchange beschäftigt. Sie erhalten Daten über den Auftragsfluss (Markt-Transaktionsprotokolle) und die aktuelle Futures.
exchange.GetRecords (englisch) Es gibt eine Reihe von Möglichkeiten, um die K-Line-Daten zu erhalten.
exchange.GetAccount (englisch) Sie haben die Daten über ihre Konten und Futures erhalten.
exchange.Buy (englisch) Der nächste Kauf ist mit einem Futures-Konto.
exchange.Sell (siehe unten) Der Verkauf wird von einem Kunden mit einer hohen Anzahl von Kunden getätigt.
exchange.GetOrder (() Sie erhalten die Bestelldaten für die angegebene ID, die auch für aktuelle Futures verfügbar sind.
exchange.GetOrders (siehe unten) Sie können sich die Liste der aktuellen Aktivitäten anmelden, die auch mit Terminkontrakten versehen ist.
exchange.CancelOrder (() Die Bestellungen mit ID-Nummern können widerrufen werden.
Futures-Objekte benötigen zusätzlich zu diesen Interfaces, die für die Umschließung von Futures-Objekten benötigt werden, zusätzliche Interface-Funktionen, die für die Umschließung von Futures verwendet werden.
exchange.SetMarginLevel (() Setzen Sie den Hebelwert für die aktuelle Sorte.
exchange.SetDirection (WEB Setzen Sie die Handelsrichtung der aktuellen Sorte wie folgt: Offene Mehrposition / Offene Mehrposition / Flachposition / Flachposition.
exchange.SetContractType (()
Setzen Sie den aktuellen Vertragskode.swap
Die Zahl der Transfers beträgt ca.quarter
Es gibt verschiedene Kontraktcodes, die auf FMZ definiert sind. Sie können die FMZ-API-Dokumentation abfragen. Diese Einstellungen müssen auch bei der Verpackung befolgt werden, sonst können alte Strategien nicht funktionieren.
exchange.GetPosition (() Hier kann man sehen, dass die aktuelle Ware keine Ware ist und dass die Ware nur durch Wechsel der Konten logisch berechnet werden kann.
Bei der AOFEX-Börse als Beispiel wird der API-KEY ausgefüllt, wenn ein Austauschobjekt mit einem allgemeinen Protokoll auf FMZ konfiguriert ist:
Das gibt es auf der General Protocol Configuration Seite auf FMZ.accessKey
undsecretKey
Einfügungsfeld, das die JSON-Formatdaten des Bodys bei der Anforderung, die das General Protocol Plugin beim Ausführen erhält, nach der Konfiguration des Universal Protocol Exchange-Objekts konfiguriertaccessKey
Die Werte der Felder sind:212f54a1-1c88-1bf5-54a1-f7bf52b3256c
,secret_key
Die Werte der Felder sind:7RJPKpBJMBkUL87RJPKpkULJPSUpsaKpUL83ysDs
。
Wenn die folgenden Interfaces aufgerufen werden, wird eine RPC-Anfrage an das General Protocol Plugin wie folgt ausgesendet:
Wenn in der Strategie aufgerufenexchange.SetMarginLevel(10)
Die Daten im angeforderten Body sind:
{
"access_key":"212f54a1-1c88-1bf5-54a1-f7bf52b3256c",
"method":"io",
"nonce":1631858961289247000,
"params":{"args":[10],"code":0},
"secret_key":"7RJPKpBJMBkUL87RJPKpkULJPSUpsaKpUL83ysDs"
}
Es wird aufgerufen exchange.SetMarginLevel ((10) und die Parameter werden in 10 übertragen.
Wenn in der Strategie aufgerufenexchange.SetDirection("buy")
Die Daten im angeforderten Body sind:
{
"access_key":"212f54a1-1c88-1bf5-54a1-f7bf52b3256c",
"method":"io",
"nonce":1631860438946922000,
"params":{"args":["buy"],"code":1},
"secret_key":"7RJPKpBJMBkUL87RJPKpkULJPSUpsaKpUL83ysDs"
}
Bei der Anforderung von exchange.SetDirection (
Wenn in der Strategie aufgerufenexchange.SetContractType("swap")
Die Daten im angeforderten Body sind:
{
"access_key":"212f54a1-1c88-1bf5-54a1-f7bf52b3256c",
"method":"io",
"nonce":1631860847525039000,
"params":{"args":["swap"],"code":2},
"secret_key":"7RJPKpBJMBkUL87RJPKpkULJPSUpsaKpUL83ysDs"
}
Die Parameter werden in "swap" übertragen.
Wenn in der Strategie aufgerufenexchange.GetPosition()
Die Daten im angeforderten Body sind:
{
"access_key":"212f54a1-1c88-1bf5-54a1-f7bf52b3256c",
"method":"io",
"nonce":1631860996119505000,
"params":{"args":[],"code":3},
"secret_key":"7RJPKpBJMBkUL87RJPKpkULJPSUpsaKpUL83ysDs"
}
Bei der Aufforderung von exchange.GetPosition (()) werden keine Parameter übermittelt.
Die json-daten in der anforderungsstelle des bodys zeigen:
method
Die Feldwerte sindio
。params
In den Felderncode
Das Urteil lautet:code
Für0
Ja, das ist es.SetMarginLevel
。code
Für1
Ja, das ist es.SetDirection
。code
Für2
Ja, das ist es.SetContractType
。code
Für3
Ja, das ist es.GetPosition
。params
Feldargs
Das ist ein großes Problem.Im Gegensatz zu der Stand-by-Version des Go-Plugins benötigen Sie mehr Informationen über die Sprache.OnPost
Die Funktion ist in der Folge zu erweitern:
Siehe folgende Code:Funktionen, die Futures-Exchange-Objekte benötigen"Die Position der Kommentare".
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
Drei Funktionen können im wörtlichen Sinne gesehen werden, die für die Einstellung der aktuellen Transaktionsart verwendet werden.SetDirection
/SetContractType
Entworfen wurde, um die aktuelle Bestellrichtung (d. h. zu lesen, um zu wissen, in welcher Richtung die Bestellung ausgeführt wird, da die Futures in zwei Richtungen eingehen: mehr und weniger, so dass eine Unterscheidung notwendig ist) und den aktuellen Vertragskode (d. h. zu ermitteln, welcher Vertrag bei der Beschaffung von Transaktionen, Aufträgen usw. eindeutig abgerufen wird) zu registrieren.
SetMarginLevel
Es ist notwendig, den Leverage-Mechanismus entsprechend der Börse zu entwerfen.
GetPosition
ist eine Lagerfunktion, die die aktuelle Sorte erhält, die direkt erstellt und auf der FMZ gesendet wird, nachdem ein General-Protokoll-Plugin die Daten zurückgegeben hat, die an die Exchange-Holding-Interface zurückgegeben wurden.position
Die gleiche Datenstruktur kann man auch verwenden.
Go-Sprache
/*
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)
}
Orbitale TiereDas ist zu schwierig.
Die Erfinder quantifizieren - Kleine TräumeFinden Sie einen Programmierer, der Ihnen in zwei Tagen hilft.