이 공용 프로토콜을 사용하여 모든 서비스를 이용할 수 있습니다
API
거래하는 거래소, 특정 API 프로토콜은 제한되지 않습니다.rest
、websocket
、fix
이 글은 모든 사람들이 사용할 수 있는 글입니다. 파이썬 일반 프로토콜의 예:https://www.fmz.com/strategy/101399
잘 작성된 http://127.0.0.1:6666/DigitalAsset
또는:http://127.0.0.1:6666/exchange
。
왜 이런 것들을 설정해야 할까요?IP、경로이봐요.
왜냐하면발명가 양자화페이지제어 센터뛰어내리기거래소 추가이 페이지에서, 일반 프로토콜 박스를 선택하여API-KEY
또한 http://127.0.0.1:6666/DigitalAsset
。DigitalAsset
이 이름은 자발적으로 정의된 이름이고, 예를 들어서 설명할 수 있습니다.
이 사이트의 주식 거래소 페이지에는 다음과 같은 내용이 있습니다.
일반적으로 거래소에서 계정 정보를 구성하는 것은access key
그리고secret key
그러나 일부 거래소의 API 인터페이스는 거래 암호를 전달하는 것을 요구합니다 (예를 들어 일부 거래소의 아래쪽 인터페이스) 이 경우 일반 프로토콜 페이지에 불필요한 컨트롤이 없기 때문에 이러한 거래소의 API를 만났을 때, 우리는 불필요한 전송이 필요한 구성 정보를secret key
이 문서는 정보에 민감하지 않은 경우access key
이 문자는 GNU 플러그인에서 문자열로 만들어집니다.split
이 작업은 이 데이터들을 분리하는 작업을 수행합니다.
그리고 플러그인에서 처리하고,XXX_PassWord
ᅳ
예를 들어, 이 글의 마지막 완전한 예에서 newBitgo 함수는 다음과 같습니다.
func newBitgo(accessKey, secretKey string) *iBitgo {
s := new(iBitgo)
s.accessKey = accessKey
s.secretKey = secretKey
// 在此可以分离secretKey中的额外配置信息,可以写成如下注释中的内容
/*
arr := strings.SplitN(secretKey, ",", 2)
if len(arr) != 2 {
panic("配置错误!")
}
s.secretKey = arr[0] // secret key
s.passWord = arr[1] // XXX_PassWord
*/
s.apiBase = "https://www.bitgo.cn"
s.timeout = 20 * time.Second
s.timeLocation = time.FixedZone("Asia/Shanghai", 8*60*60)
return s
}
전용 프로토콜 플러그인main
함수 예제:Go
언어 설명:
func main(){
var addr = flag.String("b", "127.0.0.1:6666", "bing addr") // 设置命令行参数,默认值描述,端口设置6666
flag.Parse() // 解析命令行
if *addr == "" {
flag.Usage() // 显示命令行描述
return
}
basePath := "/DigitalAsset"
log.Println("Running ", fmt.Sprintf("http://%s%s", *addr, basePath), "...") // 打印监听端口信息
http.HandleFunc(basePath, OnPost)
http.ListenAndServe(*addr, nil)
}
request
; 요청이 있을 때 응답 함수 응답 실행을 호출하고, 요청 데이터의 매개 변수를 풀고, 호스트가 전송하는 요청 데이터는:
/* request的JSON结构,发明者量化调用GetTicker,托管者发送给通用协议插件情况举例(各个API调用时,params的值可能不一样,此处method为ticker):
{
"access_key" : "XXX", // `json:"access_key"`
"secret_key" : "XXX", // `json:"secret_key"`
"nonce" : "1502345965295519602", // `json:"nonce"`
"method" : "ticker", // `json:"method"`
"params" : { // `json:"params"`
"symbol" : "btc",
...
}, // 各个请求参数略有区别。即在策略中调用不同的 发明者量化 API会有不同的参数, 在下文各个API 有介绍说明。
}
*/
그래서 JSON에 대한 배열에 따라 JSON에 대한 배열에 따라request
그 중Method
우리는 그것을 사용할 수 있습니다.switch
다른 발명가들의 양성을 처리하는 API를 분류하기 (즉, 호스트에서 실행되는 정책 호출을 식별하는 것은 어떤 발명가들의 양성을 처리하는 것인가)API
이 글은
Go
언어의 예:
switch request.Method { // 此处request.Method的M为大写,通用协议程序接收到的请求主体中为JSON数据,在Go语言中反JSON序列化(Unmarshal)为结构体,字段首字母必须大写
case "accounts" : // 当托管者上的机器人策略中调用了exchange.GetAccount()函数,托管者会发送来请求,其中Body携带的数据中method属性值为accounts
data, err = e.GetAccount(symbol)
case "ticker" :
data, err = e.GetTicker(symbol)
case "depth" :
data, err = e.GetDepth(symbol)
case "trades" :
data, err = e.GetTrades(symbol)
case "trade" :
...
default:
...
이 브랜치는 실행 후 반환된 데이터를 UPS 플러그인 프로그램이 응답해야 할 구조에 기록하여 관리자의 요청에 응답합니다.
이 언어의 예를 들어보죠.
defer func(){ // 处理收尾工作
if e := recover(); e != nil { // recover()函数用于捕获panic,e != nil即有错误发生
if ee, ok := e.(error); ok { // 类型推导,推导成功把ee.Error()赋值给e
e = ee.Error() // 调用Error方法获取返回的错误字符串
}
ret = map[string]string{"error": fmt.Sprintf("%v", e)}
}
b, _ := json.Marshal(ret) // 把本次调用获取的结果ret编码,赋值给b,写入响应指针
w.Write(b)
//fmt.Println("after w.Write the b is", string(b)) // 测试
}()
두 가지로 나눌 수 있습니다. 1. 서명 없이 사용할 수 있는 공개 인터페이스, 예를 들어:
GetTicker()
GetDepth()
GetTrades()
GetRecords(period)
…
2. 서명이 필요한 사용자 인터페이스, 예를 들어:
Buy
、Sell
GetOrder(id)
GetOrders()
GetAccount()
CancelOrder(id)
이 모든 것은
각 거래소에서 서명 방식이 다를 수 있으며 필요에 따라 작성해야합니다.
일부 발명가들은 양적 API 인터페이스를
GetName()
,GetLabel()
이 함수들은일반 프로토콜 플러그인요청서를 보내십시오.exchange.GetName()
일반 플러그인 구성의 거래소가 호출되면 "Exchange"를 반환합니다.
관리자이 문서는request
그 중method
다음을 위해:ticker
。
관리자가 전송하는 매개 변수:request.Params.symbol
: 보트 페이지 설정에 따라 관리자가 송금하는 통화.
관리자가 일반 프로토콜 플러그인을 요청할 때 주체가 가지고 있는 데이터 형식 (JSON) 을 요청한다.
{
"access_key" : "access_key",
"secret_key" : "secret_key",
"nonce" : "1500793319499", // 毫秒时间戳
"method" : "ticker",
"params" : {"symbol" : "ETH_BTC"}, // 以ETH_BTC交易对举例
}
최종적으로 관리자에게 전송되는 반환 값 구조: (즉, UTP 플러그인 요청 거래소 인터페이스가 데이터를 관리자에게 반환하는 형식)
JSON 구조
{
"data": {
"time": 1500793319499, // 毫秒时间戳,整型
"buy": 1000, // 以下浮点型
"sell": 1001,
"last": 1005,
"high": 1100,
"low": 980,
"vol": 523,
}
}
이 랩탑은request
그 중method
다음을 위해:records
관리자가 전송하는 매개 변수:request.Params.period
가치 연관성exchange.GetRecords
이 함수의 첫 번째 매개변수는request.Params.period
주기가 몇 분으로 표시되는 경우, 예를 들어 일주기는60*24
그리고 이것은1440
、request.Params.symbol
관리자가 일반 프로토콜 플러그인을 요청할 때 주체가 가지고 있는 데이터 형식을 요청합니다.
{
"access_key" : "access_key",
"secret_key" : "secret_key",
"nonce" : "1500793319499", // 毫秒时间戳
"method" : "records",
"params" : {"symbol" : "ETH_BTC", "period" : "1440"}, // 以ETH_BTC交易对,K线周期为日线举例
}
결국 관리자에게 전송되는 값 구조를 반환합니다:
JSON 구조
{
"data": [
[1500793319, 1.1, 2.2, 3.3, 4.4, 5.5], // "Time":1500793319000,"Open":1.1,"High":2.2,"Low":3.3,"Close":4.4,"Volume":5.5
[1500793259, 1.01, 2.02, 3.03, 4.04, 5.05],
...
]
}
구글의 언어 테스트 데이터:
ret_records = []interface{}{
[6]interface{}{1500793319, 1.1, 2.2, 3.3, 4.4, 5.5},
[6]interface{}{1500793259, 1.01, 2.02, 3.03, 4.04, 5.05}
}
발명가 양적 플랫폼Log
표시records
데이터:
[
{"Time":1500793319000,"Open":1.1,"High":2.2,"Low":3.3,"Close":4.4,"Volume":5.5},
{"Time":1500793259000,"Open":1.01,"High":2.02,"Low":3.03,"Close":4.04,"Volume":5.05}
]
1차원 행렬의 첫 번째 요소는int
타입, 시간
이 랩탑은request
그 중method
다음을 위해:depth
관리자가 전송하는 매개 변수:request.Params.symbol
: 관리자가 정책 설정에 따라 송금하는 통화
관리자가 일반 프로토콜 플러그인을 요청할 때 주체가 가지고 있는 데이터 형식을 요청합니다.
{
"access_key" : "access_key",
"secret_key" : "secret_key",
"nonce" : "1500793319499", // 毫秒时间戳
"method" : "depth",
"params" : {"symbol" : "ETH_BTC"}, // 以ETH_BTC交易对举例
}
결국, 관리자에게 전송되는 반환 값 구조:
JSON 구조
{
"data" : {
"time" : 1500793319499,
"asks" : [ [1000, 0.5], [1001, 0.23], [1004, 2.1], ... ],
"bids" : [ [999, 0.25], [998, 0.8], [995, 1.4], ... ],
}
}
이 랩탑은request
그 중method
다음을 위해:trades
관리자가 전송하는 매개 변수:request.Params.symbol
이 자금의 가치는 다음과 같습니다.btc
이 사이트의 주식들은, 주식 관리자들에 의해 정책 설정에 따라 송금됩니다.
관리자가 일반 프로토콜 플러그인을 요청할 때 주체가 가지고 있는 데이터 형식을 요청합니다.
{
"access_key" : "access_key",
"secret_key" : "secret_key",
"nonce" : "1500793319499", // 毫秒时间戳
"method" : "trades",
"params" : {"symbol" : "ETH_BTC"}, // 以ETH_BTC交易对举例
}
결국, 관리자에게 전송되는 반환 값 구조:
JSON 구조
{
"data": [
{
"id": 12232153,
"time" : 1529919412968,
"price": 1000,
"amount": 0.5,
"type": "buy", // "buy"、"sell"
},{
"id": 12545664,
"time" : 1529919412900,
"price": 1001,
"amount": 1,
"type": "sell",
},{
...
}
]
}
이 랩탑은request
그 중method
다음을 위해:accounts
관리자가 파러미터를 전송합니다: ((주의! 일반적으로 계정의 모든 자산을 얻습니다!, 거래소 인터페이스에 따라 개별적으로 얻거나 전체 자산 정보를 얻습니다.)
관리자가 일반 프로토콜 플러그인을 요청할 때 주체가 가지고 있는 데이터 형식을 요청합니다.
{
"access_key" : "access_key",
"secret_key" : "secret_key",
"nonce" : "1500793319499", // 毫秒时间戳
"method" : "accounts",
"params" : {},
}
결국 관리자에게 전송되는 값 구조를 반환합니다:
JSON 구조
{
"data": [
{"currency": "btc", "free": 1.2, "frozen": 0.1},
{"currency": "ltc", "free": 25, "frozen": 2.1},
{"currency": "ltc", "free": 25, "frozen": 2.1},
...
],
"raw" : {...} // 可以写入插件访问交易所时,交易所返回的原始信息(response)
}
이 랩탑은request
그 중method
다음을 위해:trade
관리자가 전송하는 매개 변수:request.Params.type
관리자: 호출에 따라exchange.Buy
아니면exchange.Sell
이 글은 전 세계 모든 나라들의 글입니다.request.Params.price
전략에서 호출:API
함수의 첫 번째 매개변수는request.Params.amount
전략에서 호출:API
이 함수의 두 번째 변수는request.Params.symbol
관리자가 일반 프로토콜 플러그인을 요청할 때 주체가 가지고 있는 데이터 형식을 요청합니다.
{
"access_key" : "access_key",
"secret_key" : "secret_key",
"nonce" : "1500793319499", // 毫秒时间戳
"method" : "trade",
"params" : {
"symbol" : "ETH_BTC",
"type" : "buy",
"price" : "1000",
"amount" : "1"
}, // 以ETH_BTC交易对,"type":"buy"买请求,价格1000,数量1举例
}
결국, 관리자에게 전송되는 반환 값 구조:
JSON 구조
{
"data": {
"id": 125456, // 下单后返回的订单id
// 如果订单id是"asdf346sfasf532"这样的字符串形式
// 此处id也可以是字符串类型
}
}
이 랩탑은request
그 중method
다음을 위해:order
관리자가 전송하는 매개 변수:request.Params.id
、request.Params.symbol
관리자가 일반 프로토콜 플러그인을 요청할 때 주체가 가지고 있는 데이터 형식을 요청합니다.
{
"access_key" : "access_key",
"secret_key" : "secret_key",
"nonce" : "1500793319499", // 毫秒时间戳
"method" : "order",
"params" : {"symbol" : "ETH_BTC", "id" : "XXX"}, // 以ETH_BTC交易对,订单id为XXX举例,注意有些交易所的订单号是数字形式的订单ID,如123456,有些交易所的订单号是字符串形式的ID,如poimd55sdfheqxv,具体看交易所的订单ID格式
}
결국, 관리자에게 전송되는 반환 값 구조:
JSON 구조
{
"data": {
"id": 2565244,
"amount": 0.15,
"price": 1002,
"status": "open", // "open":挂起状态、"closed":完成关闭状态、"cancelled":已取消
"deal_amount": 0,
"type": "buy", // "buy"、"sell"
"avg_price": 0, // 如果交易所没有提供,在处理时可以赋值为0
}
}
이 랩탑은request
메소드는 다음과 같습니다.orders
관리자가 전송하는 매개 변수:request.Params.symbol
관리자가 일반 프로토콜 플러그인을 요청할 때 주체가 가지고 있는 데이터 형식을 요청합니다.
{
"access_key" : "access_key",
"secret_key" : "secret_key",
"nonce" : "1500793319499", // 毫秒时间戳
"method" : "orders",
"params" : {"symbol" : "ETH_BTC"}, // 以ETH_BTC交易对举例
}
결국, 관리자에게 전송되는 반환 값 구조:
JSON 구조
{
"data": [{
"id": 542156,
"amount": 0.25,
"price": 1005,
"deal_amount": 0,
"type": "buy", // "buy"、"sell"
"status": "open", // "open"
},{
...
}]
}
이 랩탑은request
그 중method
다음을 위해:cancel
관리자가 전송하는 매개 변수:request.Params.id
: 문자열 유형, 정책 호출 API 함수의 첫 번째 매개 변수,request.Params.symbol
:btc ((예제) 정책 설정에 따라 주관자가 송금하는 통화
관리자가 일반 프로토콜 플러그인을 요청할 때 주체가 가지고 있는 데이터 형식을 요청합니다.
{
"access_key" : "access_key",
"secret_key" : "secret_key",
"nonce" : "1500793319499", // 毫秒时间戳
"method" : "cancel",
"params" : {"symbol" : "ETH_BTC", "id" : "XXX"}, // 以ETH_BTC交易对,id为"XXX"(同GetOrder函数的参数id一样),举例
}
결국, 관리자에게 전송되는 반환 값 구조:
JSON 구조
{
"data": true, // true or false
}
이 랩탑은request
그 중method
__api_
이 글의 제목은 "Method Name"입니다.
관리자가 일반 프로토콜 플러그인을 요청할 때 주체가 가지고 있는 데이터 형식을 요청합니다.
{
"access_key" : "access_key",
"secret_key" : "secret_key",
"nonce" : "1500793319499", // 毫秒时间戳
"method" : "__api_XXX", // XXX为具体交易所的API接口(不包含基地址)
"params" : {"borrow_id" : "123", "symbol" : "cny"}, // 具体是传入IO函数的参数
}
결국, 관리자에게 전송되는 반환 값 구조:
{
"data": {...} // 具体的接口调用的返回值
}
예를 들어, 전략 호출:
var io_str = exchange.IO("api", "POST", "cancel_borrow", "symbol=cny&borrow_id=123")
플러그인에서 테스트 코드 (go 언어):
fmt.Println("request.Method:", request.Method, "request.Params:", request.Params)
플러그인 명령줄: 2017/08/31 10:19:59 러닝http://127.0.0.1:6666/DigitalAsset …
플러그인 명령줄에서 인쇄된 request.Method,request.Params관리자가 전송한 요청은 Body에서 데이터를 분석한 후:request.Method
다음을 위해:__api_cancel_borrow
관리자가 전송한 요청은 Body에서 데이터를 분석한 후:request.Params
다음을 위해:{"borrow_id" : "123", "symbol" : "cny"}
이러한 직접 액세스 거래소를 사용자 정의 처리 할 수 있습니다API
이 모든 것은exchange.IO
전화합니다.
# 注意:
# 在调用exchange.IO("api", "POST", "/api/v1/getAccount", "symbol=BTC_USDT")时,
# 如果第二个参数不是POST,而是:exchange.IO("api", "GET", "/api/v1/getAccount", "symbol=BTC_USDT")
# 是GET方法,这时在通用协议插件接受到的http请求中头部Http-Method中储存的才是GET,
# 所以在通用协议处理IO函数实现时,需要参考以下范例代码:
// tapiCall函数定义
func (p *iStocksExchange) tapiCall(method string, params map[string]string, httpType string) (js *Json, err error) {
...
}
// 在OnPost函数中
if strings.HasPrefix(request.Method, "__api_") {
var js *Json
js, err = e.tapiCall(request.Method[6:], request.Params, r.Header.Get("Http-Method"))
...
}
하층 자동 처리exchange.GetRawJSON
이 플러그인에서는 이 앱을 사용할 필요가 없습니다.
하층 자동 처리exchange.Go
이 플러그인에서는 이러한 호출을 처리할 필요가 없습니다.
var beginTime = new Date().getTime()
var ret = exchange.Go("GetDepth")
var endTime = new Date().getTime()
Log(endTime - beginTime, "#FF0000")
// Sleep(2000)
beginTime = new Date().getTime()
Log(exchange.GetTicker())
endTime = new Date().getTime()
Log(endTime - beginTime, "#FF0000")
var depth = ret.wait()
Log("depth:", depth)
# 注意:使用exchange.Go在wait的时候如果指定了超时时间,
# 一定要确保获取到最终的数据,这样申请的并发线程才能回收。
플러그인 프로그램에서 특정 처리를 구현해야 합니다. 예를 들어, 레버리지 설정, 계약 코드, 하부 주문 방향, 로컬 변수 레코드를 설정할 수 있습니다. 보유를 획득하려면 거래소 인터페이스에 액세스하여 원자 데이터를 획득하고 FMZ 플랫폼에 정의된 보유 구조를 처리하고 반환해야합니다.
다음 함수를 호출할 때 플러그인이 수신하는Rpc
요청 형식은 다른 인터페이스와 약간 다릅니다.RpcRequest
이 형식은, 주로 params의 값으로 구분되는 복합 구조이다.
SetContractType
계약 코드를 설정합니다.
{"access_key":"123","method":"io","nonce":1623307269528738000,"params":{"args":["quarter"],"code":2},"secret_key":"123"}
설정 방향 유래상품의 아래쪽을 단방향으로 설정합니다.
{"access_key":"123","method":"io","nonce":1623308734966484000,"params":{"args":["closesell"],"code":1},"secret_key":"123"}
세트 마진 레벨 미래에 대한 레버리지를 설정합니다.
{"access_key":"123","method":"io","nonce":1623308734966939000,"params":{"args":[12],"code":0},"secret_key":"123"}
이 문서는
훗날 미래에 대한 지분을 취득합니다.
그리고exchange.GetPosition()
전화할 때:
{"access_key":"123","method":"io","nonce":1623308734967442000,"params":{"args":[],"code":3},"secret_key":"123"}
그리고exchange.GetPosition("swap")
전화할 때:
{"access_key":"123","method":"io","nonce":1623308734967442000,"params":{"args":["swap"],"code":3},"secret_key":"123"}
/*
GOOS=linux GOARCH=amd64 go build -ldflags '-s -w -extldflags -static' rest_bitgo.go
*/
package main
import (
"bytes"
"crypto/md5"
"encoding/hex"
"encoding/json"
"errors"
"flag"
"fmt"
"io/ioutil"
"log"
"net/http"
"net/url"
"sort"
"strconv"
"strings"
"time"
)
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
//panic("type assertion to int64 failed")
}
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
//panic("type assertion to float64 failed")
}
return ret
}
type iBitgo struct {
accessKey string
secretKey string
currency string
opCurrency string
baseCurrency string
secret string
secretExpires int64
apiBase string
step int64
newRate float64
timeout time.Duration
timeLocation *time.Location
}
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].Val < ms[j].Val // 按值排序
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 newBitgo(accessKey, secretKey string) *iBitgo {
s := new(iBitgo)
s.accessKey = accessKey
s.secretKey = secretKey
s.apiBase = "https://www.bitgo.cn"
s.timeout = 20 * time.Second
s.timeLocation = time.FixedZone("Asia/Shanghai", 8*60*60)
return s
}
func (p *iBitgo) apiCall(method string) (*Json, error) {
req, err := http.NewRequest("POST", fmt.Sprintf("%s/appApi.html?%s", p.apiBase, method), nil)
if err != nil {
return nil, err
}
req.Header.Set("Content-Type", "application/x-www-form-urlencoded")
resp, err := http.DefaultClient.Do(req)
if err != nil {
return nil, err
}
defer resp.Body.Close()
b, err := ioutil.ReadAll(resp.Body)
if err != nil {
return nil, err
}
return NewJson(b)
}
func (p *iBitgo) GetTicker(symbol string) (ticker interface{}, err error) {
var js *Json
js, err = p.apiCall("action=market&symbol=" + symbol)
if err != nil {
return
}
dic := js.Get("data")
ticker = map[string]interface{}{
"time": js.Get("time").MustInt64(),
"buy": dic.Get("buy").MustFloat64(),
"sell": dic.Get("sell").MustFloat64(),
"last": dic.Get("last").MustFloat64(),
"high": dic.Get("high").MustFloat64(),
"low": dic.Get("low").MustFloat64(),
"vol": dic.Get("vol").MustFloat64(),
}
return
}
func (p *iBitgo) GetDepth(symbol string) (depth interface{}, err error) {
var js *Json
js, err = p.apiCall("action=depth&symbol=" + symbol)
if err != nil {
return
}
dic := js.Get("data")
asks := [][2]float64{}
bids := [][2]float64{}
for _, pair := range dic.Get("asks").MustArray() {
arr := pair.([]interface{})
asks = append(asks, [2]float64{toFloat(arr[0]), toFloat(arr[1])})
}
for _, pair := range dic.Get("bids").MustArray() {
arr := pair.([]interface{})
bids = append(bids, [2]float64{toFloat(arr[0]), toFloat(arr[1])})
}
depth = map[string]interface{}{
"time": js.Get("time").MustInt64(),
"asks": asks,
"bids": bids,
}
return
}
func (p *iBitgo) GetTrades(symbol string) (trades interface{}, err error) {
var js *Json
js, err = p.apiCall("action=trades&symbol=" + symbol)
if err != nil {
return
}
dic := js.Get("data")
items := []map[string]interface{}{}
for _, pair := range dic.MustArray() {
item := map[string]interface{}{}
arr := pair.(map[string]interface{})
item["id"] = toInt64(arr["id"])
item["price"] = toFloat(arr["price"])
item["amount"] = toFloat(arr["amount"])
// trade.Time = toInt64(arr["time"]) * 1000
if toString(arr["en_type"]) == "bid" {
item["type"] = "buy"
} else {
item["type"] = "sell"
}
items = append(items, item)
}
trades = items
return
}
func (p *iBitgo) GetRecords(step int64, symbol string) (records interface{}, err error) {
var js *Json
js, err = p.apiCall(fmt.Sprintf("action=kline&symbol=%s&step=%d", symbol, step*60))
if err != nil {
return
}
items := []interface{}{}
for _, pair := range js.Get("data").MustArray() {
arr := pair.([]interface{})
if len(arr) < 6 {
err = errors.New("response format error")
return
}
item := [6]interface{}{}
item[0] = toInt64(arr[0])
item[1] = toFloat(arr[1])
item[2] = toFloat(arr[2])
item[3] = toFloat(arr[3])
item[4] = toFloat(arr[4])
item[5] = toFloat(arr[5])
items = append(items, item)
}
records = items
return
}
func (p *iBitgo) tapiCall(method string, params map[string]string) (js *Json, err error) {
if params == nil {
params = map[string]string{}
}
params["api_key"] = p.accessKey
h := md5.New()
h.Write([]byte(encodeParams(params, false) + "&secret_key=" + p.secretKey))
params["sign"] = strings.ToUpper(hex.EncodeToString(h.Sum(nil)))
params["action"] = method
qs := encodeParams(params, false)
req, err := http.NewRequest("POST", fmt.Sprintf("%s/appApi.html?%s", p.apiBase, qs), nil)
if err != nil {
return nil, err
}
req.Header.Set("Content-Type", "application/x-www-form-urlencoded")
resp, err := http.DefaultClient.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 js != nil {
if code := js.Get("code").MustInt64(); code != 200 {
s := js.Get("msg").MustString()
if s == "" {
s = fmt.Sprintf("%v", toString(js.data))
}
return nil, errors.New(s)
}
}
return js, err
}
func (p *iBitgo) GetAccount(symbol string) (account interface{}, err error) {
var js *Json
js, err = p.tapiCall("userinfo", nil)
if err != nil {
return
}
mp := js.Get("data")
assets := map[string]map[string]interface{}{}
for k := range mp.MustMap() {
dic := mp.Get(k)
if k == "free" {
for c := range dic.MustMap() {
if _, ok := assets[c]; !ok {
assets[c] = map[string]interface{}{}
}
assets[c]["currency"] = c
assets[c]["free"] = dic.Get(c).MustFloat64()
}
} else if k == "frozen" {
for c := range dic.MustMap() {
if _, ok := assets[c]; !ok {
assets[c] = map[string]interface{}{}
}
assets[c]["currency"] = c
assets[c]["frozen"] = dic.Get(c).MustFloat64()
}
}
}
accounts := []map[string]interface{}{}
for _, pair := range assets {
accounts = append(accounts, pair)
}
account = accounts
return
}
func (p *iBitgo) Trade(side string, price, amount float64, symbol string) (orderId interface{}, err error) {
var js *Json
js, err = p.tapiCall("trade", map[string]string{
"symbol": symbol,
"type": side,
"price": float2str(price),
"amount": float2str(amount),
})
if err != nil {
return
}
orderId = map[string]int64{"id": js.Get("orderId").MustInt64()}
return
}
func (p *iBitgo) GetOrders(symbol string) (orders interface{}, err error) {
var js *Json
js, err = p.tapiCall("entrust", map[string]string{"symbol": symbol})
if err != nil {
return
}
items := []map[string]interface{}{}
for _, ele := range js.Get("data").MustArray() {
mp := ele.(map[string]interface{})
item := map[string]interface{}{}
item["id"] = toInt64(mp["id"])
item["amount"] = toFloat(mp["count"])
if _, ok := mp["prize"]; ok {
item["price"] = toFloat(mp["prize"])
} else {
item["price"] = toFloat(mp["price"])
}
item["deal_amount"] = toFloat(mp["success_count"])
if toInt64(mp["type"]) == 0 {
item["type"] = "buy"
} else {
item["type"] = "sell"
}
item["status"] = "open"
items = append(items, item)
}
return items, nil
}
func (p *iBitgo) GetOrder(orderId int64, symbol string) (order interface{}, err error) {
var js *Json
js, err = p.tapiCall("order", map[string]string{"id": toString(orderId)})
if err != nil {
return
}
found := false
item := map[string]interface{}{}
for _, ele := range js.Get("data").MustArray() {
mp := ele.(map[string]interface{})
if toInt64(mp["id"]) != orderId {
continue
}
item["id"] = toInt64(mp["id"])
item["amount"] = toFloat(mp["count"])
if _, ok := mp["prize"]; ok {
item["price"] = toFloat(mp["prize"])
} else {
item["price"] = toFloat(mp["price"])
}
item["deal_amount"] = toFloat(mp["success_count"])
if toInt64(mp["type"]) == 0 {
item["type"] = "buy"
} else {
item["type"] = "sell"
}
switch toInt64(mp["status"]) {
case 1, 2:
item["status"] = "open"
case 3:
item["status"] = "closed"
case 4:
item["status"] = "cancelled"
}
found = true
break
}
if !found {
return nil, errors.New("order not found")
}
return item, nil
}
func (p *iBitgo) CancelOrder(orderId int64, symbol string) (ret bool, err error) {
_, err = p.tapiCall("cancel_entrust", map[string]string{"id": strconv.FormatInt(orderId, 10)})
if err != nil {
return
}
ret = true
return
}
type RpcRequest struct { // 结构体里的字段首字母必须大写,否则无法正常解析,结构体有导出和未导出,大写字母开头为导出。
// 在Unmarshal的时候会 根据 json 匹配查找该结构体的tag, 所以此处需要修饰符
AccessKey string `json:"access_key"`
SecretKey string `json:"secret_key"`
Nonce int64 `json:"nonce"`
Method string `json:"method"`
Params map[string]string `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 := newBitgo(request.AccessKey, request.SecretKey)
symbol := request.Params["symbol"]
if s := request.Params["access_key"]; len(s) > 0 {
e.accessKey = s
}
if s := request.Params["secret_key"]; len(s) > 0 {
e.secretKey = s
}
if symbolIdx, ok := map[string]int{
"btc": 1,
"ltc": 2,
"etp": 3,
"eth": 4,
"etc": 5,
"doge": 6,
"bec": 7,
}[strings.Replace(strings.ToLower(symbol), "_cny", "", -1)]; ok {
symbol = toString(symbolIdx)
}
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 := request.Params["type"]
if side == "buy" {
side = "0"
} else {
side = "1"
}
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(toInt64(request.Params["id"]), symbol)
case "cancel":
data, err = e.CancelOrder(toInt64(request.Params["id"]), symbol)
default:
if strings.HasPrefix(request.Method, "__api_") {
data, err = e.tapiCall(request.Method[6:], request.Params)
} 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:6666", "bind addr")
flag.Parse()
if *addr == "" {
flag.Usage()
return
}
basePath := "/exchange"
log.Println("Running ", fmt.Sprintf("http://%s%s", *addr, basePath), "...")
http.HandleFunc(basePath, OnPost)
http.ListenAndServe(*addr, nil)
}
초목파이썬 GUI 프로토콜 사용자 정의 거래소 예제 https://www.fmz.com/strategy/101399
초목파이썬 연결 프로토콜 사용자 정의 거래소 예 http://www.fmz.com/strategy/101399
류웨이9090bitmex 거래소는 이미 플랫폼에 접속했지만 _C (exchange.GetTicker).Buy 또는 _C (exchange.GetDepth) 를 사용하진 않습니다.
매일매일 예쁘다하하, GNU 플러그인을 작성하면 거래소 API를 완전히 연결할 수 있습니다.
자이드아직 완전히 이해되지 않았습니다... 퍼즐 플러그인을 사용하는 것은 GetAccout 같은 많은 함수를 직접 작성해야하는 구현을 의미합니까?
알란야오botVS는 js의 경우가 아닙니다. 왜 js가 없나요? 왜 GO가 있을까요?
nxtplayer6번 쏘아 올렸지만, 끝내지 않았다.
신부도 마찬가지입니다.강인함
발명가들의 수량화 - 작은 꿈BITMEX는 선물 거래소입니다. 먼저 설정해야 합니다. 당신이 작동하거나 접근하려는 계약은 API 문서 exchange.SetContractType 기능을 참조하십시오.
매일매일 예쁘다또한 ᅳ
발명가들의 수량화 - 작은 꿈하지만 만약 전략이 멀티 플랫폼이라면 어떻게 될까요? 나무 하나 때문에 숲을 포기할 수는 없습니다!
발명가들의 수량화 - 작은 꿈즉, 실행 가능한 문서로 컴파일하여 관리자와 함께 실행할 수 있습니다. 어떤 카테고리인지 중요하지 않습니다. 파이썬의 예는 아직 없는데, 시간이 있으면 하나 써보겠습니다.
발명가들의 수량화 - 작은 꿈이 정책은 특별한 처리가 필요하지 않으며 BotVS api 인터페이스의 경우, 이 거래소 객체는 동일합니다. BotVS가 액세스되었든 또는 당신이 직접 작성한 일반 프로토콜 플러그인이 지원되었든, 정책에는 아무런 조정이 필요하지 않습니다. 이 플러그인은 관리자와 함께 실행됩니다. 이 거래소에 대한 지원이 실현됩니다. 이미 액세스 된 것과 완전히 동일합니다. 위 문서를 참조하십시오: 1, GPL 플러그인을 실행, 포트 설정 이 글의 내용은 이해가 됩니다.
발명가들의 수량화 - 작은 꿈- 이 UTP 플러그인의 코드는 botvs의 API를 연결하는 동시에 실행되는 post, get 및 decrypt을 수행합니까? 네, 그렇습니다. - http://127.0.0.1:6666/DigitalAsset 이 주소는 거래소 객체를 대표하는 주소로, 관리자의 요청은 이 주소로 전송된다. 플러그인 서비스는 이 주소 요청에 귀를 기울이고, 응답하여 관리자의 대신 거래소를 방문하고 관리자의 요청된 데이터를 반환한다.
발명가들의 수량화 - 작은 꿈파이썬으로 구현한 것과 동일합니다. 인터페이스와 형식도 동일합니다. 이 문헌은 한글에 쓰여진 문헌으로 구성되어 있습니다.
발명가들의 수량화 - 작은 꿈이것은 호스팅하는 외부 플러그인 형태의 예시로, BotVS 플랫폼에 액세스하는 데 대한 일부 거래소 상호 작용 코드를 작성하기 위한 것입니다. 당신은 PHP, node.js, python, Go 등의 언어를 사용하여 이러한 종류의 플러그인을 작성할 수 있습니다. 데이터 형식이 위의 문서 설명 형식과 일치하는 한, 플러그인이 얻은 데이터는 해당 BotVS API 인터페이스에 연결될 수 있습니다.
발명가들의 수량화 - 작은 꿈예, BotVS API를 구현하는 GetAccount과 같은 이러한 인터페이스 기능의 구체적인 구현을 작성하는 것은 연결된 거래소의 코드를 작성하는 것과 같습니다. BotVS에 연결하여 편리하게 통일 정책을 호출하십시오.
발명가들의 수량화 - 작은 꿈예를 들어, 파이썬, 노드.js, PHP, 골랑을 사용할 수 있습니다.