Вы можете использовать этот протокол для доступа к любому из доступных сервисов.
API
Обмены, на которых торгуется, не ограничены конкретными протоколами API, независимо от того,rest
、websocket
、fix
... может быть доступен и использован. Пример общих протоколов Python:https://www.fmz.com/strategy/101399
Хорошо написанный адрес прослушивания и портовые настройки для прослушивания.
Например:http://127.0.0.1:6666/DigitalAsset
Или:http://127.0.0.1:6666/exchange
。
Почему это нужно?ИП、ПутьЧто это?
Потому чтоИзобретатель количественныйСтраницаЦентр управленияПрыжкиДобавить биржуНа странице, выберите кнопку "Общие протоколы", показывающую толькоAPI-KEY
, а также адресная строка сервиса, которая сообщает администратору о том, на какой IP и порту он должен получить доступ (администратор и плагин-программа не могут работать на одном устройстве).http://127.0.0.1:6666/DigitalAsset
。DigitalAsset
Это самоопределённое название, и это всего лишь пример.
На странице обменных площадок Quantitative Inventor's Add-on выглядит так:
Обычно для конфигурации учетной записи на бирже требуется только конфигурацияaccess key
иsecret key
Однако API-интерфейсы некоторых бирж требуют передачи пароля сделки (например, нижний интерфейс некоторых бирж), поэтому, поскольку на странице общих протоколов нет лишних контролей для записи этой информации, когда мы сталкиваемся с API таких бирж, мы можем записать лишнюю конфигурационную информацию, которая должна быть передана.secret key
Если информация не является конфиденциальной, она может быть написана вaccess key
И затем вы делаете строку в плагинах General Protocol.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-сериализации.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:
...
После выполнения этих ветвей возвращенные данные записываются в структуру, на которую должен отвечать плагин-программа общих протоколов, отвечая на запрос администратора.
Пример языка Go:
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交易对举例
}
В конечном итоге отправляется хранителю возвращаемой стоимости структуры: (т.е. формат, в котором данные, запрошенные обменным интерфейсом после просьбы обмена плагином об универсальном протоколе, возвращаются хранителю)
Структура 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],
...
]
}
Данные теста на язык Go:
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}
]
Примечание: первый элемент в матрице второго измеренияint
Тип, который представляет собой время нажатия. 2.
В данном случае, мы можем использовать функцию, которую высылает администратор для ответа на прослушку.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_
Название метода, начинающегося с.
Формат данных, которые ведут субъекты, когда администраторы запрашивают плагин-протокол
{
"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 Runninghttp://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
Формат запроса немного отличается от других интерфейсов, необходимо обратить внимание на плагин-программу в General ProtocolRpcRequest
Формат, в основном отличающийся парами, является сложной структурой.
SetContractType
Настройка контрактного кода.
{"access_key":"123","method":"io","nonce":1623307269528738000,"params":{"args":["quarter"],"code":2},"secret_key":"123"}
SetDirection Настройка фьючерса в одностороннем направлении.
{"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"}
GetPosition
Покупать фьючерсные пакеты.
Когда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)
}
ТраваПример обменной системы с использованием протокола Python Access.
ТраваПример обменной системы с использованием протокола Python http://www.fmz.com/strategy/101399
Лиувей9090Битмекс присоединился к платформе, но не смог получить покупку с помощью _C (exchange.GetTicker).Buy или _C (exchange.GetDepth).
Каждый день.Ха-ха, написать плагин-протокол общих протоколов можно самостоятельно, полностью соединяя API бирж, или лучше просто использовать собственные программы.
Zhchy751Есть ли примеры готовых доступных протоколов, полных роботов/стратегий/конфигурации бирж и т.д.?
ДюхуЭтот код - это плагин-протокол, не так ли? Этот плагин не похож на плагин-протокол, который я могу прямо вызвать? Я отправляю и получаю данные и анализирую их с помощью py post или get. Этот плагин-протокол, который выполняет пост, получает и расшифровывает, а также получает botvs.api?
кальянЕсли у вас есть Python, это хорошо.
соломинкаНе совсем понятно... Использование плагинов универсальных программ означает ли, что вам нужно написать много функций самостоятельно, например, GetAccout)))?
АланьяоbotVS - это не больше, чем примеры js? Почему нет js?
nxtplayer6 лет летят, но ничего не делают.
И невесты тоже.Ожесточенный
Изобретатели количественного измерения - мечтыBITMEX - это фьючерсная биржа. Для того, чтобы получить доступ к контрактам, которые вы хотите использовать или получить, см. API-документы exchange.SetContractType.
Каждый день.И это правда.
Изобретатели количественного измерения - мечтыНо что, если стратегия будет мультиплатформенной?
Изобретатели количественного измерения - мечтыИ это означает, что вы можете скомпилировать его в исполняемый файл, который вы можете запустить вместе с хостером. Какой каталог не имеет значения. Я не знаю, как это сделать, но у меня есть несколько примеров Python, которые я написал.
ДюхуКак плагин работает, например, записывается в формате py, прямо в каталоге exe хостера? Или добавляется ссылка на путь в каталог файлов py в локальной программе py?
Изобретатели количественного измерения - мечтыВ политике нет особой обработки, для интерфейса BotVS API, объекты этих бирж одинаковы (независимо от того, доступны ли BotVS или поддерживаются ваши собственные плагины), политика не требует каких-либо изменений. Смотрите документацию выше: 1, запуск плагина общих протоколов, настройка портов В этом случае мы не можем сказать, что мы не знаем.
ДюхуЭто похоже на шаблон стратегии, где экспозиция, которая хочет связаться с плагином, вызывает эту функцию, а другие экспозиции используют существующий botvs api.
Изобретатели количественного измерения - мечты- Код этого универсального протокола плагинов, который был реализован в post, get и декодируется, а также соединяется с API botvs? Да, это так. - http://127.0.0.1:6666/DigitalAsset Этот адрес представляет собой объект биржи, к которому отправляются запросы администратора. Плагин-сервис прослушивает запрос на этот адрес и реагирует, чтобы вместо администратора посетить биржу и вернуть запрошенные администратором данные (формат), реализация, стратегия бота совместимы с какой-либо BotVS и еще не подключенной бирже.
Изобретатели количественного измерения - мечтыЭто то же самое, что и в Python, интерфейс, формат. В этом случае, если вы не знаете языка, то вы не знаете языка, который используется.
Изобретатели количественного измерения - мечтыЭто пример формы внешнего хост-плагина, целью которого является написание некоторого интерактивного кода биржи для доступа к платформе BotVS. Вы можете использовать языки PHP, node.js, python, Go и т. д. Для написания такого плагина, при условии, что формат данных соответствует формату, описанному выше, данные, полученные плагином, могут быть связаны с соответствующим интерфейсом BotVS API.
Изобретатели количественного измерения - мечтыДа, для написания конкретных реализаций таких функций интерфейса, как BotVS API GetAccount, достаточно написать код для связанной биржи.
Изобретатели количественного измерения - мечтыНапример, Python, Node.js, PHP и Golang могут быть использованы.