Recentemente, os usuários conversaramAOFEX
Este é o Exchange, considerando que no FMZ ainda não há um exemplo de como conectar a interface REST do contrato do Exchange.AOFEX
Por exemplo, explica como acessar o contrato Exchange.
No FMZ, há uma distinção entre o mercado de ações e o mercado de futuros. Por exemplo, os três principais mercados que conhecemos são o mercado de ações e o mercado de contratos. As interfaces de APIs desses diferentes mercados também são diferentes, e até mesmo alguns sistemas de APIs são completamente separados e independentes.
Existem muitos exemplos para o uso do protocolo geral para encapsular o Exchange no FMZ, mas não há um exemplo completo para encapsular um contrato no Exchange.
O documento do protocolo geral FMZ:https://www.fmz.com/bbs-topic/1052O documento inclui um protocolo geral de acesso ao DEMO para o mercado de ações
Interfaces principais para objetos de trocas instantâneas
exchange.GetTicker (em inglês) O que é mais importante é que você tenha acesso aos dados do mercado de ticks, que são futuros em tempo real.
exchange.GetDepth (em inglês) O que é que isso tem a ver com o mercado de ações?
exchange.GetTrades (em inglês) Os dados sobre o fluxo de pedidos (registro de transações no mercado) e os futuros no momento também estão disponíveis.
exchange.GetRecords (em inglês) A partir de agora, o mercado de ações está a crescer, e o mercado de ações está a crescer.
exchange.GetAccount (em inglês) O site do governo do estado de São Paulo (SSA) também divulgou dados de ativos da conta, incluindo futuros em dinheiro.
exchange.Buy (em inglês) O que é que isso tem a ver com o nosso país?
exchange.Sell (em inglês) O que é que a gente tem aqui?
exchange.GetOrder (em inglês) Os dados de pedidos com o ID especificado são obtidos, incluindo os futuros em tempo real.
exchange.GetOrders (em inglês) Para obter a lista pendente da atividade atual, os futuros em dinheiro também estão disponíveis.
exchange.CancelOrder (em inglês) O pedido de ID especificado foi cancelado, e os futuros estão disponíveis.
Os objetos de câmbio de futuros requerem funções de interface adicionais para os objetos de câmbio de futuros, além dessas interfaces necessárias para envelopar os objetos de câmbio de futuros.
exchange.SetMarginLevel (em inglês) Configure a alavancagem da variedade atual.
exchange.SetDirection (em inglês) Configure a direção de negociação para a variedade atual, ou seja: posições abertas / posições abertas / posições flácidas / posições flácidas.
exchange.SetContractType (em inglês)
Configure o código do contrato atual.swap
A taxa de transferência é de aproximadamentequarter
As configurações de acordo com o código de contrato definido no FMZ podem ser consultadas na documentação do FMZ API. Também é necessário seguir essas configurações no momento do envio, caso contrário, as políticas anteriores podem não funcionar corretamente.
exchange.GetPosition (em inglês) Obtenha dados de posicionamento da variedade atual. Aqui pode-se ver que o presente não tem o conceito de posicionamento, o presente só pode ser calculado logicamente pela variação da conta de contraste. Mas os futuros têm posicionamento.
No exemplo da troca AOFEX, se o objeto da troca configurado no protocolo geral no FMZ for preenchido, a API KEY é:
A página de configuração do protocolo geral no FMZ temaccessKey
esecretKey
Caixa de entrada, que configura o objeto de troca do protocolo geral após o plug-in do protocolo geral, recebe dados de formato JSON do corpo em uma solicitação quando o aplicativo é executado.accessKey
O valor do campo é:212f54a1-1c88-1bf5-54a1-f7bf52b3256c
,secret_key
O valor do campo é:7RJPKpBJMBkUL87RJPKpkULJPSUpsaKpUL83ysDs
。
Quando a seguinte interface é chamada, o administrador envia uma solicitação RPC para o plugin do protocolo geral:
Quando a política é chamadaexchange.SetMarginLevel(10)
Quando o usuário faz o login, os dados no corpo solicitado são:
{
"access_key":"212f54a1-1c88-1bf5-54a1-f7bf52b3256c",
"method":"io",
"nonce":1631858961289247000,
"params":{"args":[10],"code":0},
"secret_key":"7RJPKpBJMBkUL87RJPKpkULJPSUpsaKpUL83ysDs"
}
Quando o exchange.SetMarginLevel é chamado, o parâmetro é transmitido para 10.
Quando a política é chamadaexchange.SetDirection("buy")
Quando o usuário faz o login, os dados no corpo solicitado são:
{
"access_key":"212f54a1-1c88-1bf5-54a1-f7bf52b3256c",
"method":"io",
"nonce":1631860438946922000,
"params":{"args":["buy"],"code":1},
"secret_key":"7RJPKpBJMBkUL87RJPKpkULJPSUpsaKpUL83ysDs"
}
A função de seleção é executada através de um comando de direção.
Quando a política é chamadaexchange.SetContractType("swap")
Quando o usuário faz o login, os dados no corpo solicitado são:
{
"access_key":"212f54a1-1c88-1bf5-54a1-f7bf52b3256c",
"method":"io",
"nonce":1631860847525039000,
"params":{"args":["swap"],"code":2},
"secret_key":"7RJPKpBJMBkUL87RJPKpkULJPSUpsaKpUL83ysDs"
}
O "Swap" é um parâmetro que é transmitido para "swap" através da chamada de "exchange.SetContractType" (
Quando a política é chamadaexchange.GetPosition()
Quando o usuário faz o login, os dados no corpo solicitado são:
{
"access_key":"212f54a1-1c88-1bf5-54a1-f7bf52b3256c",
"method":"io",
"nonce":1631860996119505000,
"params":{"args":[],"code":3},
"secret_key":"7RJPKpBJMBkUL87RJPKpkULJPSUpsaKpUL83ysDs"
}
Não há nenhum parâmetro sendo transmitido quando a exchange.GetPosition ((() é chamada.
Observando os dados do JSON no request body acima, descobrimos:
method
O valor do campo éio
。params
No campocode
A decisão é a seguinte:code
Para0
Sim, é verdade.SetMarginLevel
。code
Para1
Sim, é verdade.SetDirection
。code
Para2
Sim, é verdade.SetContractType
。code
Para3
Sim, é verdade.GetPosition
。params
O campoargs
O que você está fazendo?Em comparação com o exemplo do plug-in de linguagem Go disponível, o plug-in precisa ser executado emOnPost
Expanda a função:
O código abaixo é:Funções de expansão necessárias para objetos de câmbio de futuros"Posição da nota".
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
Três funções podem ser vistas literalmente como sendo usadas para configurar as respectivas configurações da variedade de transação atual.SetDirection
/SetContractType
De acordo com o design, é possível definir uma variável local para registrar a direção atual do pedido (ou seja, ler essa configuração ao fazer o pedido para saber em que direção o pedido deve ser feito, porque os futuros são comprados em duas direções: muito e pouco espaço, por isso é necessário distinguir) e o código do contrato atual (quando se obtém a transação, o pedido e outras operações, é preciso consultar claramente qual é o contrato).
SetMarginLevel
O mecanismo de alavancagem precisa ser projetado de acordo com o mecanismo de alavancagem da bolsa (parâmetros de alavancagem são transmitidos como parâmetros na interface abaixo);
GetPosition
É uma função de armazenamento que obtém a variedade atual, quando o plug-in do protocolo geral obtém os dados retornados para a interface de armazenamento da bolsa, é construída diretamente e no FMZ.position
A mesma estrutura de dados também pode ser usada.
Língua 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)
}
Biologia da órbitaEu não sei, é difícil.
Inventor quantificado - sonho pequenoEncontre um programador e em dois dias ele vai resolver tudo.