Las conversaciones de los usuarios recientesAOFEX
Este intercambio, teniendo en cuenta que FMZ aún no tiene un ejemplo de cómo conectar una interfaz REST a un intercambio contratado.AOFEX
Por ejemplo, explica cómo acceder a los contratos de Exchange.
En FMZ, hay una distinción entre los intercambios de efectivo y los intercambios de futuros. Por ejemplo, los tres principales mercados conocidos, los intercambios de efectivo y los contratos, también tienen interfaces diferentes de las API de los diferentes mercados, e incluso algunos sistemas de API son completamente independientes.
En la comunidad de la plataforma FMZ, ya hay muchos ejemplos documentales para usar el protocolo general para envasar el contado de Exchange con FMZ, pero no hay un ejemplo completo de envasar un contrato de Exchange.
En la actualidad, la mayoría de las personas que viven en la zona son de origen indígena.https://www.fmz.com/bbs-topic/1052El documento incluye un protocolo general de acceso a DEMO para los intercambios de efectivo.
Interfaz principal de los objetos de los intercambios de efectivo
En la actualidad, la mayoría de las aplicaciones de Exchange.GetTicker están disponibles. Los ticks son una herramienta que permite obtener datos sobre el mercado y los futuros en efectivo.
exchange.GetDepth (en inglés) En la actualidad, hay una gran cantidad de datos de pedidos y futuros en efectivo.
exchange.GetTrades (en inglés) Los datos de flujo de pedidos (los registros de transacciones en el mercado) y los futuros en efectivo también están disponibles.
exchange.GetRecords (en inglés) Los futuros en efectivo también están disponibles para obtener datos de la línea K.
exchange.GetAccount (en inglés) Los datos de los activos de las cuentas, incluidos los futuros en efectivo, también están disponibles.
En cambio. Compra En el siguiente pago, los futuros en efectivo estarán disponibles.
exchange.Sell (en inglés) Los anuncios de venta y los futuros en efectivo también están disponibles.
En el caso de los usuarios de Exchange.GetOrder. Para obtener los datos de los pedidos con el ID especificado, los futuros en efectivo también están disponibles.
exchange.GetOrders (en inglés) Para obtener el listado de la actividad actual, los futuros en efectivo también están disponibles.
En el caso de los usuarios de Exchange.CancelOrder Los pedidos de ID especificados pueden ser cancelados, y los futuros en efectivo también.
Los objetos de los mercados de futuros requieren una función de interfaz adicional para los objetos de los mercados de futuros, además de estas interfaces que requieren el envase de los objetos de los mercados de futuros.
Exchange.SetMarginLevel también está disponible. Establece el apalancamiento de la variedad actual.
Exchange.SetDirection (en inglés) La dirección de negociación de la variedad actual es: posiciones abiertas / posiciones abiertas / posiciones abiertas / posiciones abiertas / posiciones abiertas.
exchange.SetContractType (en inglés)
Establece el código del contrato actual.swap
En la actualidad, el número de refugiados en el país es de alrededor de 1.000.quarter
Las configuraciones de la API de FMZ se pueden consultar en la documentación de la API de FMZ. También se deben seguir estas configuraciones al empacar, de lo contrario, las políticas anteriores pueden no funcionar correctamente.
exchange.GetPosition (en inglés) Obtener los datos de tenencia de la variedad actual. Aquí se puede ver que el concepto de tenencia no existe, y que el actual solo puede calcular la tenencia lógica mediante cambios en las cuentas de contraste.
Tomando como ejemplo el intercambio AOFEX, si el objeto del intercambio está configurado con el protocolo general en FMZ, el API KEY que se debe completar es:
En la página de configuración del protocolo general en FMZaccessKey
ysecretKey
Cuadro de entrada que configura el objeto de intercambio de protocolo general después de que el plug-in de protocolo general se ejecute en la solicitud que recibe el cuerpo de datos en formato JSONaccessKey
El valor del campo es212f54a1-1c88-1bf5-54a1-f7bf52b3256c
,secret_key
El valor del campo es7RJPKpBJMBkUL87RJPKpkULJPSUpsaKpUL83ysDs
。
Cuando se llama a la siguiente interfaz, el administrador envía la siguiente solicitud de RPC al plugin del protocolo general:
Cuando se llama en la políticaexchange.SetMarginLevel(10)
En el caso de los datos en el cuerpo solicitado, los datos son:
{
"access_key":"212f54a1-1c88-1bf5-54a1-f7bf52b3256c",
"method":"io",
"nonce":1631858961289247000,
"params":{"args":[10],"code":0},
"secret_key":"7RJPKpBJMBkUL87RJPKpkULJPSUpsaKpUL83ysDs"
}
Se llama exchange.SetMarginLevel ((10), y se transmite el parámetro 10.
Cuando se llama en la políticaexchange.SetDirection("buy")
En el caso de los datos en el cuerpo solicitado, los datos son:
{
"access_key":"212f54a1-1c88-1bf5-54a1-f7bf52b3256c",
"method":"io",
"nonce":1631860438946922000,
"params":{"args":["buy"],"code":1},
"secret_key":"7RJPKpBJMBkUL87RJPKpkULJPSUpsaKpUL83ysDs"
}
Se llama exchange.SetDirection ((
Cuando se llama en la políticaexchange.SetContractType("swap")
En el caso de los datos en el cuerpo solicitado, los datos son:
{
"access_key":"212f54a1-1c88-1bf5-54a1-f7bf52b3256c",
"method":"io",
"nonce":1631860847525039000,
"params":{"args":["swap"],"code":2},
"secret_key":"7RJPKpBJMBkUL87RJPKpkULJPSUpsaKpUL83ysDs"
}
Se llama exchange.SetContractType (
Cuando se llama en la políticaexchange.GetPosition()
En el caso de los datos en el cuerpo solicitado, los datos son:
{
"access_key":"212f54a1-1c88-1bf5-54a1-f7bf52b3256c",
"method":"io",
"nonce":1631860996119505000,
"params":{"args":[],"code":3},
"secret_key":"7RJPKpBJMBkUL87RJPKpkULJPSUpsaKpUL83ysDs"
}
Cuando se llama exchange.GetPosition ((), no se transmite ningún parámetro.
Observando los datos de JSON en el cuerpo de la solicitud anterior, se encuentra:
method
El valor del campo esio
。params
En el campocode
El juicio es el siguiente:code
¿Por qué?0
Sí, es cierto.SetMarginLevel
。code
¿Por qué?1
Sí, es cierto.SetDirection
。code
¿Por qué?2
Sí, es cierto.SetContractType
。code
¿Por qué?3
Sí, es cierto.GetPosition
。params
En el campoargs
En el centro.En comparación con los ejemplos de plugins de lenguaje Go disponibles, los plugins de lenguaje Go se necesitan en una versión más larga.OnPost
En la función, extienda:
En el siguiente código se puede ver:Extensión de las funciones necesarias para los objetos de la bolsa de futurosLa posición de los comentarios.
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
Las tres funciones se pueden ver literalmente como las configuraciones correspondientes a la variedad de transacciones en curso.SetDirection
/SetContractType
Desde el punto de vista del diseño, se trata de establecer una variable local para registrar la dirección actual de la orden (es decir, leer esta configuración al momento de realizar la orden para saber en qué dirección la orden se desea realizar, ya que los futuros compran en dos direcciones: mucho espacio y espacio, por lo que se necesita una distinción) y el código del contrato actual (cuando se obtiene la transacción, la orden y otras operaciones, se consulta claramente qué contrato es).
SetMarginLevel
El mecanismo de apalancamiento debe ser diseñado de acuerdo con el mecanismo de apalancamiento de la bolsa.
GetPosition
Es una función de almacenamiento que obtiene la variedad actual y se construye directamente y en FMZ después de que un plugin de protocolo general obtiene los datos devueltos a la interfaz de almacenamiento de la bolsa.position
La misma estructura de datos.
Lenguaje 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.(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)
}
Bios de la trayectoriaEs muy difícil.
Los inventores cuantifican - sueños pequeñosEncuentra un programador y lo arreglarás en dos días.