Erfinder Quantitative Handelsplattform allgemeiner Protokoll Zugang zu kundenspezifischen Börsen

Schriftsteller:Kleine Träume, Erstellt: 2017-08-24 16:29:56, aktualisiert: 2021-06-10 15:33:02

Dokumentation für die Verwendung des allgemeinen Protokolls

Sie können mit diesem Protokoll auf alle Dienste zugreifen.APIEs gibt keine Grenzen für die spezifischen API-Protokollen, die von den Handelshändlern verwendet werden.restwebsocketfix... können alle zugreifen und nutzen. Ein Beispiel für den Python-Generalprotokoll:https://www.fmz.com/strategy/101399

  • 1. Universal-Protokoll-Plug-in läuft, Port-Einstellungen

    Die Überwachungsadresse und die Port-Einstellungen für das GNU-Plug-in-Tablets sind gut geschrieben. Ein Beispiel:http://127.0.0.1:6666/DigitalAssetOder:http://127.0.0.1:6666/exchange

    Warum sollte man das tun?EigentumsrechtWegWas ist das? WeilDer Erfinder quantifiziertSeiteDas KontrollzentrumSpringenZusätzliche BörsenSie werden auf der Seite, auf der Sie die Option General Protocol wählen, angezeigt.API-KEYEine weitere Funktion ist die Angabe der IP-Adresse, die dem Administrator mitteilt, wann er auf welche IP-Adresse und auf welchen Port zugreifen soll.http://127.0.0.1:6666/DigitalAssetDigitalAssetDas ist ein selbstdefinierter Name, nur ein Beispiel.

    Auf der Inventor Quantitative Add Exchange-Seite sieht es so aus: Normalerweise müssen die Kontoinformationen der Börsen nur konfiguriert werden.access keyundsecret keyDie API-Schnittstellen einiger Börsen verlangen jedoch die Übermittlung des Transaktionscodes (z. B. die Unter-Schnittstelle einiger Börsen), so dass wir die überflüssigen Konfigurationsinformationen, die übermittelt werden müssen, in der API eines solchen Austauschs schreiben können, da es keine überflüssigen Steuerelemente auf der UPC-Seite gibt, um diese Informationen zu schreiben.secret keyWenn die Informationen nicht sensibel sind, kann man sie inaccess keyUnd dann machen Sie eine String in einem General Protocol Plugin.splitDie Daten werden dann von einem anderen Computer verwendet, um die Daten zu trennen, wie in diesem Beispiel.

    img

    Und dann wird es in einem Plugin verarbeitet, und man bekommtXXX_PassWordIch bin nicht derjenige. Zum Beispiel in der letzten vollständigen Vorlage dieses Beitrags, die neue Bitgo-Funktion:

    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
    }
    

    img

    Ein allgemeines Protokoll-PluginmainFunktionsbeispiel:GoDie Sprache beschreibt:

    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)
    }
    
  • 2. Die Antwortfunktion

    Die Anzeige von Anfragen, die von den Anwendern auf den angegebenen Ports gesendet werden, wird ständig überwachtrequestEine Antwortfunktion wird aufgerufen, um die Parameter in den Request-Daten zu analysieren. Die Request-Daten, die vom Host gesendet werden:

    /* 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 有介绍说明。
    }
    */
    

    Also nach den Anfragen, die im General Protocol Plugin empfangen werden, erhalten Sie die Strukturen, die nach der Reverse-JSON-Sequenzierung der Body-Daten erhalten werden.requestInnerlichMethodWir können es benutzen.switchDie API, die für die Quantifizierung von verschiedenen Erfindern sortiert wird (d.h. die auf einem Host laufenden Strategienanrufe identifiziert, welche Erfinder quantifiziert wurden)APISie rufen:

    GoEin Beispiel für Sprache:

    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:
          ...
    

    Diese Branchen schreiben die zurückgegebenen Daten, nachdem sie ausgeführt wurden, in eine Struktur, die von einem Universal-Protokoll-Plugin-Programm beantwortet werden soll, und beantworten die Anfragen der Treuhänder.

    Ein Beispiel für 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))     // 测试
      }()
    
  • 3. Art der API-Aufrufe

    Sie sind in zwei Kategorien unterteilt: 1. öffentliche Schnittstellen, die keine Unterschrift erfordern, wie:

    GetTicker()

    GetDepth()

    GetTrades()

    GetRecords(period)

    2. Benutzeroberflächen, für die eine Unterschrift erforderlich ist, z. B.:

    BuySell

    GetOrder(id)

    GetOrders()

    GetAccount()

    CancelOrder(id)Ich bin nicht derjenige. Die Unterschrift kann von Börse zu Börse unterschiedlich sein und muss bedarfsgerecht geschrieben werden.

  • 4. Die Erfinder quantifizieren die Anrufe der verschiedenen API-SchnittstellenPlugins für das allgemeine ProtokollundTreuhänderDie Interaktionsformate sind:

    Einige Erfinder quantifizierten API-Schnittstellen wieGetName()GetLabel()Eine gleichmäßige Funktion wird nicht aufgerufen.Plugins für das allgemeine ProtokollSenden Sie eine Anfrage.exchange.GetName()Die Anrufe an die Börse mit der allgemeinen Plugin-Konfiguration geben "Exchange" zurück.

    • 1, GetTicker:Die Daten werden für den aktuellen Markt verwendet.

      TreuhänderDie Funktion wird von der Funktion "Aufhörung" übertragen.requestInnerlichmethodFür:ticker

      Der Administrator sendet die Parameter:request.Params.symbol: Die Währung, die von den Administratoren gemäß den Einstellungen auf der Bots-Seite gesendet wird.

      Datenformat, das der Subjekt trägt (JSON), wenn ein Administrator ein Universal-Protokoll-Plugin anfordert

      {
          "access_key" : "access_key",
          "secret_key" : "secret_key",
          "nonce" :      "1500793319499",            // 毫秒时间戳
          "method" :     "ticker",
          "params" :     {"symbol" : "ETH_BTC"},     // 以ETH_BTC交易对举例
      }
      

      Endgültig an den Trustee gesendete Rückwertstruktur: (d.h. Format, in dem die Daten, die ein UTP-Plugin nach der Anforderung einer Austauschoberfläche an den Trustee zurückgibt)

      JSON-Struktur

      {
          "data": {
              "time": 1500793319499,  // 毫秒时间戳,整型
              "buy": 1000,            // 以下浮点型
              "sell": 1001,
              "last": 1005,
              "high": 1100,
              "low": 980,
              "vol": 523,
          }
      }
      
    • 2, GetRecords:Für die Erfassung von K-Line-Daten, die von den Börsen bereitgestellt werden.

      Die Funktion wird von einem Administrator an eine Überwachungs-Antwort-Funktion gesendet.requestInnerlichmethodFür:records

      Der Administrator sendet die Parameter:request.Params.periodWerteverknüpfungenexchange.GetRecordsDie erste Parameter der Funktion, tatsächlich.request.Params.periodEine Periode, die in Minuten angegeben ist, wie z.B. eine Tageszyklose, wird als60*24Das heißt1440request.Params.symbolDer Treuhänder sendet nach der eingestellten Währung.

      Datenformat, das der Subjekt trägt, wenn ein Administrator ein Universal-Protokoll-Plug-in anfordert

      {
          "access_key" : "access_key",
          "secret_key" : "secret_key",
          "nonce" :      "1500793319499",            // 毫秒时间戳
          "method" :     "records",
          "params" :     {"symbol" : "ETH_BTC", "period" : "1440"},     // 以ETH_BTC交易对,K线周期为日线举例
      }
      

      Das Ergebnis ist ein sehr schwieriger Prozess, denn es ist eine sehr schwierige Aufgabe.

      JSON-Struktur

      {
          "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],
                  ...
          ]
      }
      

      Die Daten des Go-Sprach-Tests:

      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}
      }
      

      Erfinder-QuantifizierungsplattformLogangezeigtrecordsDie Daten:

      [
          {"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}
      ]
      

      Also, das ist das erste Element in einer zweidimensionalen Matrix.intDer Typ, der für die Zeitspanne steht, wird von den Administratoren automatisch multipliziert mit 1000 und mehr angezeigt.

    • 3 ̊ GetDepth:Erfahren Sie mehr über die Börse (Orders thin, sell one, sell two... buy one, buy two...)

      Die Funktion wird von einem Administrator an eine Überwachungs-Antwort-Funktion gesendet.requestInnerlichmethodFür:depth

      Der Administrator sendet die Parameter:request.Params.symbol: von den Treuhandgebern in einer Währung gesendet, die von der Strategie festgelegt wird

      Datenformat, das der Subjekt trägt, wenn ein Administrator ein Universal-Protokoll-Plug-in anfordert

      {
          "access_key" : "access_key",
          "secret_key" : "secret_key",
          "nonce" :      "1500793319499",            // 毫秒时间戳
          "method" :     "depth",
          "params" :     {"symbol" : "ETH_BTC"},     // 以ETH_BTC交易对举例
      }
      

      Das Ergebnis ist eine Struktur, die an die Treuhänder zurückgeschickt wird:

      JSON-Struktur

      {
          "data" : {
              "time" : 1500793319499,
              "asks" : [ [1000, 0.5], [1001, 0.23], [1004, 2.1], ... ],
              "bids" : [ [999, 0.25], [998, 0.8], [995, 1.4], ... ],
          }
      }
      
    • 4. GetTrades:Zugriff auf Transaktionsprotokolle für die gesamte Zeit, die die Börse zur Verfügung stellt (ohne eigene Transaktionsprotokolle)

      Die Funktion wird von einem Administrator an eine Überwachungs-Antwort-Funktion gesendet.requestInnerlichmethodFür:trades

      Der Administrator sendet die Parameter:request.Params.symbolDer Wert der Währungen, in denen sie gehandelt werden, ist:btcDas Programm wird von den Treuhandgebern in einer Währung gesendet, die von den Treuhandverwaltern entsprechend der von ihnen festgelegten Strategie festgelegt wird.

      Datenformat, das der Subjekt trägt, wenn ein Administrator ein Universal-Protokoll-Plug-in anfordert

      {
          "access_key" : "access_key",
          "secret_key" : "secret_key",
          "nonce" :      "1500793319499",            // 毫秒时间戳
          "method" :     "trades",
          "params" :     {"symbol" : "ETH_BTC"},     // 以ETH_BTC交易对举例
      }
      

      Das Ergebnis ist eine Struktur, die an die Treuhänder zurückgeschickt wird:

      JSON-Struktur

      { 
          "data": [
              {
                  "id": 12232153,
                  "time" : 1529919412968,
                  "price": 1000,
                  "amount": 0.5,
                  "type": "buy",             // "buy"、"sell"
              },{
                  "id": 12545664,
                  "time" : 1529919412900,
                  "price": 1001,
                  "amount": 1,
                  "type": "sell",
              },{
                  ...
              }
          ]
      }
      
    • 5, GetAccount:Erhalten Sie Informationen über Kontovermögen

      Die Funktion wird von einem Administrator an eine Überwachungs-Antwort-Funktion gesendet.requestInnerlichmethodFür:accounts

      Der Treuhänder sendet die Parameter: ((Hinweis! im Allgemeinen werden alle Vermögenswerte des Kontos abgerufen!, je nachdem, ob die Börsenoberfläche einzeln oder insgesamt Informationen über die Vermögenswerte enthält)

      Datenformat, das der Subjekt trägt, wenn ein Administrator ein Universal-Protokoll-Plug-in anfordert

      {
          "access_key" : "access_key",
          "secret_key" : "secret_key",
          "nonce" :      "1500793319499",            // 毫秒时间戳
          "method" :     "accounts",
          "params" :     {},                         
      }
      

      Das Ergebnis ist ein sehr schwieriger Prozess, denn es ist eine sehr schwierige Aufgabe.

      JSON-Struktur

      {
          "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)
      }
      
    • 6, Kauf, Verkauf:Versenden von Aufträgen, Abwicklung von Transaktionen.

      Die Funktion wird von einem Administrator an eine Überwachungs-Antwort-Funktion gesendet.requestInnerlichmethodFür:trade

      Der Administrator sendet die Parameter:request.Params.typeVerwalter auf Anrufexchange.BuyOderexchange.SellIch habe es nicht verstanden.request.Params.priceIn der Strategie:APIDie erste Parameter der Funktion.request.Params.amountIn der Strategie:APIDer zweite Parameter der Funktionrequest.Params.symbolDer Treuhänder sendet nach der eingestellten Währung.

      Datenformat, das der Subjekt trägt, wenn ein Administrator ein Universal-Protokoll-Plug-in anfordert

      {
          "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举例
      }
      

      Das Ergebnis ist eine Struktur, die an die Treuhänder zurückgeschickt wird:

      JSON-Struktur

      {
          "data": {
              "id": 125456,      // 下单后返回的订单id
                                 // 如果订单id是"asdf346sfasf532"这样的字符串形式
                                 // 此处id也可以是字符串类型
          }
      }
      
    • 7 ≈ GetOrder:Erhalten Sie die Bestellinformationen für die angegebene Bestellnummer

      Die Funktion wird von einem Administrator an eine Überwachungs-Antwort-Funktion gesendet.requestInnerlichmethodFür:order

      Der Administrator sendet die Parameter:request.Params.idrequest.Params.symbol

      Datenformat, das der Subjekt trägt, wenn ein Administrator ein Universal-Protokoll-Plug-in anfordert

      {
          "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格式
      }
      

      Das Ergebnis ist eine Struktur, die an die Treuhänder zurückgeschickt wird:

      JSON-Struktur

      { 
          "data": {
              "id": 2565244,
              "amount": 0.15,
              "price": 1002,
              "status": "open",    // "open":挂起状态、"closed":完成关闭状态、"cancelled":已取消
              "deal_amount": 0,
              "type": "buy",       // "buy"、"sell"
              "avg_price": 0,      // 如果交易所没有提供,在处理时可以赋值为0
          }
      }
      
    • 8 ‘Get Orders:Erhalten Sie alle unvollendeten Bestellungen

      Die Funktion wird von einem Administrator an eine Überwachungs-Antwort-Funktion gesendet.requestDie Methode lautet:orders

      Der Administrator sendet die Parameter:request.Params.symbol

      Datenformat, das der Subjekt trägt, wenn ein Administrator ein Universal-Protokoll-Plug-in anfordert

      {
          "access_key" : "access_key",
          "secret_key" : "secret_key",
          "nonce" :      "1500793319499",            // 毫秒时间戳
          "method" :     "orders",
          "params" :     {"symbol" : "ETH_BTC"},     // 以ETH_BTC交易对举例
      }
      

      Das Ergebnis ist eine Struktur, die an die Treuhänder zurückgeschickt wird:

      JSON-Struktur

      {
          "data": [{
              "id": 542156,
              "amount": 0.25,
              "price": 1005,
              "deal_amount": 0,
              "type": "buy",      // "buy"、"sell"
              "status": "open",   // "open"
          },{
              ...
          }]
      }
      
    • 9, Auftrag stornierenAuftragsverfügung für die angegebene Bestellnummer stornieren

      Die Funktion wird von einem Administrator an eine Überwachungs-Antwort-Funktion gesendet.requestInnerlichmethodFür:cancel

      Der Administrator sendet die Parameter:request.Params.idDer erste Parameter der API-Funktion, die von der Strategie aufgerufen wird, ist der Typ des Strings.request.Params.symbol:btc (beispiel) wird von der Treuhänderin in der von ihr eingestellten Währung gesendet

      Datenformat, das der Subjekt trägt, wenn ein Administrator ein Universal-Protokoll-Plug-in anfordert

      {
          "access_key" : "access_key",
          "secret_key" : "secret_key",
          "nonce" :      "1500793319499",            // 毫秒时间戳
          "method" :     "cancel",
          "params" :     {"symbol" : "ETH_BTC", "id" : "XXX"},     // 以ETH_BTC交易对,id为"XXX"(同GetOrder函数的参数id一样),举例
      }
      

      Das Ergebnis ist eine Struktur, die an die Treuhänder zurückgeschickt wird:

      JSON-Struktur

      {
          "data": true,        // true or false
      }
      
    • 10 IO:调用发明者量化平台的exchange.IO函数

      Die Funktion wird von einem Administrator an eine Überwachungs-Antwort-Funktion gesendet.requestInnerlichmethodFür__api_Der Name der Methode beginnt mit.

      Datenformat, das der Subjekt trägt, wenn ein Administrator ein Universal-Protokoll-Plug-in anfordert

      {
          "access_key" : "access_key",
          "secret_key" : "secret_key",
          "nonce" :      "1500793319499",            // 毫秒时间戳
          "method" :     "__api_XXX",                // XXX为具体交易所的API接口(不包含基地址)
          "params" :     {"borrow_id" : "123", "symbol" : "cny"},      // 具体是传入IO函数的参数
      }
      

      Das Ergebnis ist eine Struktur, die an die Treuhänder zurückgeschickt wird:

      {
          "data": {...}       // 具体的接口调用的返回值
      }
      

      Ein Beispiel für Strategieanrufe ist:

      var io_str = exchange.IO("api", "POST", "cancel_borrow", "symbol=cny&borrow_id=123")
      

      Der Testcode für das Plugin ((go Sprache):

      fmt.Println("request.Method:", request.Method, "request.Params:", request.Params)
      

      Plugin Kommandozeile: 2017/08/31 10:19:59 Runninghttp://127.0.0.1:6666/DigitalAsset

      Request.Method,request.Params, die in der Plugin-Befehlszeile gedruckt werdenIn der Request, die der Administrator nach der Datenanalyse im Body sendet:request.MethodFür:__api_cancel_borrowIn der Request, die der Administrator nach der Datenanalyse im Body sendet:request.ParamsFür:{"borrow_id" : "123", "symbol" : "cny"}

      Sie können diese direkt zugänglichen Börsen anpassen.APIDieexchange.IOIch habe einen Anruf gemacht.

      # 注意:
      # 在调用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的支持

      Automatische Bearbeitung der Unterseiteexchange.GetRawJSONDie Anrufe von Plugins müssen nicht in Plugins implementiert werden.

    • 对于exchange.Go的支持

      Automatische Bearbeitung der Unterseiteexchange.GoDie Anrufe werden nicht in Plugins verarbeitet.

      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)
      

      img

      img

      # 注意:使用exchange.Go在wait的时候如果指定了超时时间, 
      #      一定要确保获取到最终的数据,这样申请的并发线程才能回收。
      
    • Unterstützung für Futures-Funktionen

      Spezifische Abläufe müssen im Plugin implementiert werden, wie z.B. das Setzen von Leverage, Vertragskode, Aborderrichtung, die Einrichtung eines lokalen Variablenprotokolls, die Erfassung von Haltungen, die Zugriff auf die Börsenoberfläche, die Erfassung von Rohdaten und die Bearbeitung der für die FMZ-Plattform definierten Haltungsstruktur. Wenn die folgenden Funktionen in der Strategie aufgerufen werden, wird das Plugin die folgenden Informationen erhalten:RpcDie Anforderungsformate unterscheiden sich etwas von anderen Interfaces und müssen in den Plugins für das allgemeine Protokoll beachtet werden.RpcRequestDas Format, von dem die Params-Werte hauptsächlich unterschieden werden, ist eine komplexe Struktur.

      • SetContractType wird angezeigt
        Sie haben die Code-Kontrakte eingerichtet.

        {"access_key":"123","method":"io","nonce":1623307269528738000,"params":{"args":["quarter"],"code":2},"secret_key":"123"}
        
      • SetDirection Die Futures werden in eine Richtung gesetzt.

        {"access_key":"123","method":"io","nonce":1623308734966484000,"params":{"args":["closesell"],"code":1},"secret_key":"123"}
        
      • Set Margin Level Sie haben die Möglichkeit, Futures-Leverage einzusetzen.

        {"access_key":"123","method":"io","nonce":1623308734966939000,"params":{"args":[12],"code":0},"secret_key":"123"}
        
      • GetPosition Die Annahme von Futures-Holdings. Wennexchange.GetPosition()Sie werden von der Polizei befragt.

        {"access_key":"123","method":"io","nonce":1623308734967442000,"params":{"args":[],"code":3},"secret_key":"123"}
        

        Wennexchange.GetPosition("swap")Sie werden von der Polizei befragt.

        {"access_key":"123","method":"io","nonce":1623308734967442000,"params":{"args":["swap"],"code":3},"secret_key":"123"}
        

  • Vollständiges Go-Sprachmodell für das Universalprotokoll-Plugin (z.B. Zugriff auf Bitcoins)

/*
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)
}


Mehr

Das GrasBeispiel für eine benutzerdefinierte Exchange für Python Access Protocol https://www.fmz.com/strategy/101399

Das GrasBeispiel für eine benutzerdefinierte Exchange für den Python Access Protocol http://www.fmz.com/strategy/101399

Weiwei9090Die Bitmex-Börse ist bereits auf die Plattform zugegriffen, aber es ist unabhängig davon, ob sie _C (exchange.GetTicker).Buy oder _C (exchange.GetDepth) verwenden.

Jeden Tag eine schlanke ModeHa ha, man kann ein Universal-Protokoll-Plugin schreiben, um die API vollständig selbst zu verknüpfen, oder man kann es einfach mit einem eigenen Programm machen.

Zhchy751Gibt es bereits vorhandene Beispiele für gpr-zugriff, komplette Roboter / Strategien / Börsenkonfigurationen usw.?

Das ist alles.Der Code ist ein Protokoll-Plugin, oder? Das ist nicht so ähnlich wie ein Strategie-Plugin, das ich direkt aufrufen kann. Ich werde mit py post oder get senden und erhalten und analysieren.

SchallieWenn man Python hat, ist das gut.

SchleimhautIch habe noch nicht ganz verstanden... bedeutet das, dass man viele Funktionen selbst schreiben muss, wenn man ein Universal-Plugin verwendet, z. B. GetAccout (?).

AlanyaobotVS ist nicht immer ein mehrerer von JS-Problemen? Warum kein JS?

nxtplayerIch habe 6 Flugzeuge, aber sie fliegen nicht ab.

Auch die BrautDie Bullen

Kleine TräumeBITMEX ist eine Futures-Börse. Sie müssen die Funktion SetContractType einstellen, um die Art der Verträge zu bedienen oder zu erhalten, die Sie benötigen.

Jeden Tag eine schlanke ModeDas stimmt auch.

Kleine TräumeAber was ist, wenn die Strategie eine Multi-Plattform ist?

Kleine TräumeDas ist komponierbar in eine ausführbare Datei, die mit einem Administrator ausgeführt werden kann. Es spielt keine Rolle, welcher Katalog. Ich habe noch keine Beispiele für Python, aber ich habe mir Zeit genommen, eine zu schreiben.

Das ist alles.Wie lässt sich ein Plugin zum Beispiel in py-Format schreiben, direkt in den Host-exe-Verzeichnis? Oder fügt man im lokalen py-Programm einen Bezugspfad zum py-Dateiverzeichnis des Plugins hinzu?

Kleine TräumeFür die BotVS-API-Schnittstelle sind die Austauschobjekte identisch (ob BotVS bereits zugegriffen ist oder ob Sie ein eigenes General-Protokoll-Plugin geschrieben haben, das unterstützt wird). Siehe oben Dokumentation: 1, Universal Protocol Plugin läuft, Port-Einstellungen Ich bin der Meinung, dass es eine gute Idee ist, den Inhalt zu verstehen.

Das ist alles.Ist es nicht ähnlich wie bei der Strategie-Vorlage, bei der die entsprechende Plugin-Börse die Funktion hier aufruft und bei der anderen die bestehende Botvs-API verwendet?

Kleine Träume- Ist der Code für dieses General-Protocol-Plug-in die Implementierung von post, get und decompile, die gleichzeitig die API von botvs verknüpft? Ja, das ist wahr. - http://127.0.0.1:6666/DigitalAsset Diese Adresse ist für die Exchange-Objekte, an die Anfragen der Administratoren gesendet werden. Ein Plugin-Dienstleistungsprogramm hört auf die Anfragen der Adresse und reagiert, um anstelle des Administrators auf die Exchange zuzugreifen und die von den Administratoren angeforderten Daten zurückzugeben.

Kleine TräumeDie Implementierung in Python ist die gleiche, die Schnittstelle ist die gleiche, das Format ist das gleiche. Es ist nur eine Sprache, die unterschiedlich ist.

Kleine TräumeEs handelt sich um ein Beispiel für ein externes Host-Plugin, das den Zweck hat, Interaktionscode für die BotVS-Plattform zu schreiben, was einer automatischen Anbindung an die BotVS entspricht. Man kann solche Plugins in PHP, Node.js, Python, Go und anderen Sprachen schreiben, sofern die Datenformate den Dokumentationsformaten entsprechen.

Kleine TräumeJa, um die Implementierung der BotVS API GetAccount zu schreiben, ist es so, als würde man den Code für die verknüpfte Börse selbst schreiben.

Kleine TräumeDas ist ein Beispiel für die Verwendung von Python, Node.js, PHP und Golang.