Fortgeschrittene Anleitung für die FMZ Quant-Plattform Strategie schreiben

Schriftsteller:- Ich bin ein Idiot., Erstellt: 2022-03-22 09:00:57, Aktualisiert: 2022-03-29 10:02:52

[TOC] Bevor Sie dieses Tutorial lernen, müssen Sie studierenMit der FMZ Quant-Plattform beginnenundGrundlegendes Tutorial für die FMZ Quant-Plattform Strategie schreiben, und in Programmiersprachen beherrschen.Das elementare Tutorial deckt die am häufigsten verwendeten Funktionen ab, aber es gibt viele Funktionen und Funktionen, die nicht eingeführt wurden, und sie werden in diesem Tutorial nicht behandelt.Nach dem Erlernen dieses Tutorials, werden Sie in der Lage sein, mehr kostenlose und angepasste Strategien zu schreiben, und FMZ Quant Plattform ist nur ein Werkzeug.

Zugriff auf die Rohdaten der Plattform

Die FMZ Quant-Plattform umfasst alle unterstützten Plattformen. Um die Einheitlichkeit zu erhalten, ist unsere Unterstützung für eine einzige Plattform-API noch nicht vollständig. Zum Beispiel kann GetRecords die Anzahl der K-Zeilen oder die Startzeit übermitteln, während sie auf der FMZ-Plattform festgelegt ist; einige Plattformen unterstützen Batch-Ordering, während FMZ dies nicht unterstützt, und so weiter. Daher ist eine Möglichkeit erforderlich, direkt auf die Plattformdaten zuzugreifen.Für öffentliche Schnittstellen (z. B. Marktnotierungen) können SieHttpQuery, und für verschlüsselte Schnittstellen (mit Kontoinformationen), müssen SieIO.Für spezifische eingehende Parameter, wenden Sie sich bitte an die entsprechende Plattform API Dokument.InfoDas Feld gibt Rohinformationen zurück, aber es macht immer noch keinen Unterschied für das Problem der Nichtunterstützung von Schnittstellen.

GetRawJSON ((()

Es gibt den Rohinhalt (Strings) zurück, der von der letzten REST-API angefordert wurde, die verwendet werden kann, um die erweiterten Informationen selbst zu analysieren.

function main(){
    var account = exchange.GetAccount() //the account doesn't contain all data returned by the request
    var raw = JSON.parse(exchange.GetRawJSON())//raw data returned by GetAccount()
    Log(raw)
}

HttpQuery() Zugriff auf öffentliche Schnittstellen

Um auf öffentliche Schnittstellen zuzugreifen, kann JsHttpQuery, und Python kann verwandte Pakete verwenden, wieurlliboderrequests.

HttpQuery setzt standardmäßig die GET-Methode ein und unterstützt mehr Funktionen. Weitere Details finden Sie im API-Dokument.

var exchangeInfo = JSON.parse(HttpQuery('https://api.binance.com/api/v1/exchangeInfo'))
Log(exchangeInfo)
var ticker = JSON.parse(HttpQuery('https://api.binance.com/api/v1/ticker/24hr'))
var kline = JSON.parse(HttpQuery("https://www.quantinfo.com/API/m/chart/history?symbol=BTC_USD_BITFINEX&resolution=60&from=1525622626&to=1561607596"))

Beispiel für Python mit Anfragen:

import requests
resp = requests.get('https://www.quantinfo.com/API/m/chart/history?symbol=BTC_USD_BITFINEX&resolution=60&from=1525622626&to=1561607596')
data = resp.json()

IO Zugriff auf verschlüsselte Schnittstellen

Für Schnittstellen, die API-Key-Signaturen erfordern, kann die IO-Funktion verwendet werden, und die Benutzer müssen sich nur um die eingehenden Parameter kümmern, und der spezifische Signaturprozess wird von der Unterschicht abgeschlossen.

Die FMZ-Plattform unterstützt derzeit keine BitMEX-Stop-Loss-Orders, die über IO gemäß den folgenden Schritten implementiert werden können:

  • zuerst finden Sie die Anweisungsseite der BitMEX API:https://www.bitmex.com/api/explorer/;
  • Dann finden Sie die Bestelladresse von BitMEX unter:https://www.bitmex.com/api/v1/order, mit der MethodePOST; wenn FMZ die Basisadresse bereits intern angegeben hat, müssen Sie nur /api/v1/order eingeben.
  • entsprechende Parameter:symbol=XBTUSD&side=Buy&orderQty=1&stopPx=4000&ordType=Stop.

Spezifischer Code:

var id = exchange.IO("api", "POST", "/api/v1/order", "symbol=XBTUSD&side=Buy&orderQty=1&stopPx=4000&ordType=Stop")
// You can also pass in the object 
var id = exchange.IO("api", "POST", "/api/v1/order", "", JSON.stringify({symbol:"XBTUSD",side:"Buy",orderQty:1,stopPx:4000,ordType:"Stop"}))

Weitere IO-Beispiele:https://www.fmz.com/bbs-topic/3683

Websocket verwendet

Grundsätzlich unterstützen alle Kryptowährungsplattformen Websocket zum Senden von Marktzitaten und einige Plattformen Websocket zur Aktualisierung von Kontoinformationen. Im Vergleich zu Rest API hat Websocket im Allgemeinen die Vorteile, wie niedrige Latenzzeit, hohe Frequenz und nicht durch die Frequenz der Plattform Rest API eingeschränkt zu sein, etc. Der Nachteil ist, dass es ein Unterbrechungsproblem gibt, dessen Verarbeitung nicht intuitiv ist.

Dieser Artikel wird vor allem vorstellen, wie man die JavaScript-Sprache verwendet und wie man die Dial-Funktion verwendet, die von der Plattform verkapselt ist, um auf der FMZ Quant-Plattform zu verbinden; für spezifische Anweisungen und Parameter sind im Dokument, können Sie nach Dial suchen; um verschiedene Funktionen zu realisieren, wurde die Dial-Funktion mehrmals aktualisiert. Dieser Artikel wird dies abdecken und vermittelt ereignisgesteuerte Strategien auf der Grundlage von wss, sowie die Frage der Verbindung mehrerer Plattformen. Python kann auch die Dial-Funktion oder die entsprechende Bibliothek verwenden.

1. Websocket-Verbindung

Im Allgemeinen verbinden Sie sich direkt über Websocket; zum Beispiel, um Binance Tricker Push zu erhalten:

var client = Dial("wss://stream.binance.com:9443/ws/!ticker@arr")

Wenn die zurückgegebenen Daten in einem komprimierten Format sind, sollte bei der Verbindung eine Angabe vorgenommen werden; compress bezieht sich auf das komprimierte Format und mode stellt dar, welche zurückgegebenen Daten komprimiert werden müssen; beispielsweise bei der Verbindung mit OKEX:

var client = Dial("wss://real.okex.com:10441/websocket?compress=true|compress=gzip_raw&mode=recv")

Die Dial-Funktion unterstützt die Wiederverbindung, die durch den zugrunde liegenden Golang durchgeführt wird. Wenn die erkannte Verbindung ausfällt, wird sie wieder verbunden. Für die Anforderungsdaten, die bereits in der URL enthalten sind, wie zum Beispiel Binance gerade, ist es sehr praktisch und empfehlenswert. Für diejenigen, die Abonnementnachrichten senden müssen, können sie den Wiederverbindungsmechanismus selbst pflegen.

var client = Dial("wss://stream.binance.com:9443/ws/!ticker@arr|reconnect=true")

Für das Abonnieren von wss-Nachrichten sind einige Plattformanfragen in der URL, und einige müssen die abonnierten Kanäle selbst senden, wie Coinbase:

client = Dial("wss://ws-feed.pro.coinbase.com", 60)
client.write('{"type": "subscribe","product_ids": ["BTC-USD"],"channels": ["ticker","heartbeat"]}')

2. Verschlüsselte Schnittstellenverbindung

Im Allgemeinen wird Websocket zum Lesen von Marktnoten verwendet, kann aber auch zum Erhalten von Aufträgen und Konto-Push verwendet werden. Der Push solcher verschlüsselten Daten hat manchmal eine lange Verzögerung und sollte mit Vorsicht verwendet werden. Da die Verschlüsselungsmethode komplizierter ist, finden Sie hier einige Beispiele zur Referenz. Beachten Sie, dass nur AccessKey erforderlich ist, der als Strategieparameter festgelegt werden kann. Wenn SecretKey erforderlich ist, kann es implizit von der Exchange aufgerufen werden.

    //Push example of Huobi Futures
    var ACCESSKEYID = 'accesskey of your Huobi account'
    var apiClient = Dial('wss://api.hbdm.com/notification|compress=gzip&mode=recv')
    var date = new Date(); 
    var now_utc =  Date.UTC(date.getUTCFullYear(), date.getUTCMonth(), date.getUTCDate(),date.getUTCHours(), date.getUTCMinutes(), date.getUTCSeconds());
    var utc_date = new Date(now_utc)
    var Timestamp = utc_date.toISOString().substring(0,19)
    var quest = 'GET\napi.hbdm.com\n/notification\n'+'AccessKeyId='+ACCESSKEYID+'&SignatureMethod=HmacSHA256&SignatureVersion=2&Timestamp=' + encodeURIComponent(Timestamp)
    var signature = exchange.HMAC("sha256", "base64", quest, "{{secretkey} }") // Remove the extra blank spaces between }}
    auth = {op: "auth",type: "api",AccessKeyId: ACCESSKEYID, SignatureMethod: "HmacSHA256",SignatureVersion: "2", Timestamp: Timestamp, Signature:encodeURI(signature)}
    apiClient.write(JSON.stringify(auth))
    apiClient.write('{"op": "sub","cid": "orders","topic": "orders.btc'}')
    while (true){
        var data = datastream.read()
        if('op' in data && data.op == 'ping'){
            apiClient.write(JSON.stringify({op:'pong', ts:data.ts}))
        }
    }
    
    // Push example of Binance; pay attention that listenKey needs to be updated regularly   
    var APIKEY = 'accesskey of your Binance account'
    var req = HttpQuery('https://api.binance.com/api/v3/userDataStream',{method: 'POST',data: ''},null,'X-MBX-APIKEY:'+APIKEY);
    var listenKey = JSON.parse(req).listenKey;
    HttpQuery('https://api.binance.com/api/v3/userDataStream', {method:'DELETE',data:'listenKey='+listenKey}, null,'X-MBX-APIKEY:'+APIKEY);
    listenKey = JSON.parse(HttpQuery('https://api.binance.com/api/v3/userDataStream','',null,'X-MBX-APIKEY:'+APIKEY)).listenKey;
    var datastream = Dial("wss://stream.binance.com:9443/ws/"+listenKey+'|reconnect=true',60);
    var update_listenKey_time =  Date.now()/1000;
    while (true){
        if (Date.now()/1000 - update_listenKey_time > 1800){
            update_listenKey_time = Date.now()/1000;
            HttpQuery('https://api.binance.com/api/v3/userDataStream', {method:'PUT',data:'listenKey='+listenKey}, null,'X-MBX-APIKEY:'+APIKEY);
        }
        var data = datastream.read()
    }

    // push example of BitMEX
    var APIKEY = "your Bitmex API ID"
    var expires = parseInt(Date.now() / 1000) + 10
    var signature = exchange.HMAC("sha256", "hex", "GET/realtime" + expires, "{{secretkey} }")// secretkey is automatically replaced during execution, so no need to fill in
    var client = Dial("wss://www.bitmex.com/realtime", 60)
    var auth = JSON.stringify({args: [APIKEY, expires, signature], op: "authKeyExpires"})
    var pos = 0
    client.write(auth)
    client.write('{"op": "subscribe", "args": "position"}')
    while (true) {
        bitmexData = client.read()
        if(bitmexData.table == 'position' && pos != parseInt(bitmexData.data[0].currentQty)){
            Log('position change', pos, parseInt(bitmexData.data[0].currentQty), '@')
            pos = parseInt(bitmexData.data[0].currentQty)
        }
    }

3. Websocket-Lesen

Im Allgemeinen kann es kontinuierlich in einer unendlichen Schleife gelesen werden.

function main() {
    var client = Dial("wss://stream.binance.com:9443/ws/!ticker@arr");
    while (true) {
        var msg = client.read()
        var data = JSON.parse(msg) // Parse json strings into quotable objects 
// Process data 
    }
}

Die Geschwindigkeit des WSS-Datenpushes ist sehr schnell. Die Unterschicht von Golang speichert alle Daten in der Warteschlange und wenn der Programmruf gelesen wird, werden die Daten wiedergegeben. Allerdings verursachen Operationen wie das Platzieren einer Bestellung auf dem Bot Verzögerungen, was zu einer Anhäufung von Daten führen kann. Für Informationen wie Handelsausführungspush, Konto-Push und Tiefeninterpolationspush benötigen wir die Historiendaten. Für Marktdaten kümmern wir uns in den meisten Fällen nur um die neuesten Daten, nicht um Historiendaten.

Wennread()wenn keine Parameter hinzugefügt werden, wird es die ältesten Daten zurückgeben, und blockieren bis zurück, wenn es keine Daten gibt.client.read(-2)die neuesten Daten sofort zurückzugeben, aber wenn keine Daten vorhanden sind, wird null zurückgegeben, was vor der Referenz beurteilt werden muss.

Je nachdem, wie man mit den alten zwischengespeicherten Daten umgeht und ob sie blockiert werden, wenn keine Daten vorhanden sind, hat read verschiedene Parameter, wie in der folgenden Tabelle gezeigt, was kompliziert aussieht, aber das Programm flexibler macht.img

4.Verbindung mit mehreren Plattformen per Websocket

In diesem Fall ist es offensichtlich, dass die einfache Verwendung von read() im Programm nicht funktioniert, da eine Plattform Wartenachrichten blockiert, und eine andere Plattform selbst wenn es neue Nachrichten gibt, nicht empfängt.

    function main() {
        var binance = Dial("wss://stream.binance.com:9443/ws/!ticker@arr");
        var coinbase = Dial("wss://ws-feed.pro.coinbase.com", 60)
        coinbase.write('{"type": "subscribe","product_ids": ["BTC-USD"],"channels": ["ticker","heartbeat"]}')
        while (true) {
            var msgBinance = binance.read(-1) // Parameter -1 represents no data and return null immediately; it will not occur that being blocked before there is data to be returned 
            var msgCoinbase = coinbase.read(-1)
            if(msgBinance){
                // at this time, Binance has data to return 
            }
            if(msgCoinbase){
                // at this time, coinbase has data to return 
            }
            Sleep(1) // Sleep for 1 millisecond
        }
    }

5. Problematik der Trennung und Wiederaufnahme

Dieser Teil der Verarbeitung ist problematischer, da die Push-Daten unterbrochen werden können oder die Push-Verzögerung extrem lang ist. Auch wenn der Herzschlag empfangen werden kann, bedeutet dies nicht, dass die Daten immer noch gedrückt werden. Sie können ein Ereignisintervall festlegen; wenn nach dem Intervall kein Update empfangen wird, verbinden Sie sich erneut; es ist am besten, die Ergebnisse, die von rest nach einer bestimmten Zeit zurückgegeben werden, zu vergleichen, um zu sehen, ob die Daten korrekt sind. Für die speziellen Fälle von Binance können Sie direkt eine automatische Wiederverbindung festlegen.

6.Verwenden des allgemeinen Programmrahmens von Websocket

Für die Push-Daten verwendet wurde, wird das Programm natürlich als Ereignis ausgelöst geschrieben werden; achten Sie auf die Häufigkeit der Push-Daten, weil Hochfrequenz-Anfragen führen zu blockiert werden; im Allgemeinen können Sie schreiben:

    var tradeTime = Date.now()
    var accountTime = Date.now()
    function trade(data){
        if(Date.now() - tradeTime > 2000){//Here it limits only one trade in 2 seconds 
            tradeTime = Date.now()
            // Trading logic
        }
    }
    function GetAccount(){
        if(Date.now() - accountTime > 5000){//Here it limits GetAccount only once in 5 seconds 
            accountTime = Date.now()
            return exchange.GetAccount()
        }
    }
    function main() {
        var client = Dial("wss://stream.binance.com:9443/ws/!ticker@arr|reconnect=true");
        while (true) {
            var msg = client.read()
            var data = JSON.parse(msg)
            var account = GetAccount()
            trade(data)
        }
    }

7.Conclusion

Die Verbindungsmethode, die Datenübertragungsmethode, der abonnierte Inhalt und das Datenformat des Websockets auf jeder Plattform sind oft unterschiedlich, so dass die Plattform sie nicht einkapselt und die Dial-Funktion verwenden muss, um sich selbst zu verbinden.

PS: Obwohl einige Plattformen keine Websocket-Zitate anbieten, werden Sie bei der Anmeldung auf der Website feststellen, dass sie alle den Websocket-Push verwenden. Nach der Recherche werden Sie feststellen, dass einige Abonnementformate und Rückgabeformate verschlüsselt zu sein scheinen, was durch Entschlüsselung und Dekomprimierung mit base64 zu sehen ist.

Mehrthread-Konkurrenz

JavaScript kann die Konkurrenz durch die Go-Funktion realisieren, und Python kann die entsprechende Multithread-Bibliothek verwenden.

Bei der Umsetzung von quantitativen Strategien kann die gleichzeitige Ausführung die Zeitverzögerung reduzieren und die Effizienz verbessern. Nehmen wir den Hedging-Strategie-Bot als Beispiel. Er muss die Tiefe von zwei Münzen erhalten, und der in der Reihenfolge ausgeführte Code wird wie folgt dargestellt:

var depthA = exchanges[0].GetDepth()
var depthB = exchanges[1].GetDepth()

Wenn eine Anforderung von Rest-API verzögert wird, zum Beispiel die verzögerte Zeit ist 100 Millisekunden, dann ist die Zeit für die Gewinnung der Tiefe zweimal tatsächlich anders; wenn mehr Zugriffe benötigt werden, werden die Verzögerungsprobleme offensichtlicher, die die Ausführung der Strategie beeinflussen wird.

Da JavaScript kein Multithread hat, kann die Go-Funktion in der Unterschicht verwendet werden, um dieses Problem zu lösen.GetDepth, GetAccountund so weiter.IOwird auch unterstützt, wie z. B.:exchange.Go("IO", "api", "POST", "/api/v1/contract_batchorder", "orders_data=" + JSON.stringify(orders)), aber aufgrund des Konstruktionsmechanismus ist die Umsetzung mühsamer.

var a = exchanges[0].Go("GetDepth")
var b = exchanges[1].Go("GetDepth")
var depthA = a.wait() // Call "wait" method to wait for the return of the asynchronous GetDepth result 
var depthB = b.wait()

In den meisten einfachen Fällen ist das Schreiben von Strategien auf diese Weise in Ordnung. Beachten Sie jedoch, dass der Prozess jedes Mal wiederholt wird, wenn die Strategie-Schleife läuft, und die Zwischenvariablen a und b sind eigentlich nur vorübergehend hilfsbedürftig. Wenn wir viele gleichzeitige Aufgaben haben, müssen wir zusätzlich die Korrespondenz zwischen a und depthA, b und depthB aufzeichnen. Wenn unsere gleichzeitigen Aufgaben unsicher sind, ist die Situation komplizierter. Daher hoffen wir, eine Funktion zu realisieren: Wenn wir die Go-Funktion gleichzeitig schreiben, binden wir eine Variable zur gleichen Zeit; wenn das gleichzeitige Laufresultat zurückkehrt, wird der Ergebniswert automatisch der Variable zugewiesen, wodurch die Notwendigkeit, Zwischenvariablen zu eliminieren und das Programm präziser zu machen, beseitigt wird. Die konkrete Implementierung ist wie folgt:

function G(t, ctx, f) {
    return {run:function(){
        f(t.wait(1000), ctx)
    }}
}

Wir haben eine G-Funktion definiert, bei der der Parameter t die auszuführende Go-Funktion ist, ctx die Funktion des Aufzeichnungsprogrammkontexts ist und f die Funktion ist, die einen bestimmten Wert zuweist.

Zu diesem Zeitpunkt kann der Gesamtprogrammrahmen als Modell geschrieben werden, ähnlich dem Produzent-Verbraucher Modell (mit einigen Unterschieden), der Produzent sendet kontinuierlich Aufgaben aus und der Verbraucher führt sie gleichzeitig aus.

var Info = [{depth:null, account:null}, {depth:null, account:null}] // If we need to obtain the depth and account of the two platforms, more information can also be put in, such as order ID and status, etc.
var tasks = [ ] // Global task list 

function produce(){ // Issue all kinds of concurrent tasks
  // Here the task producing logic has been omitted, only for demo
  tasks.push({exchange:0, ret:'depth', param:['GetDepth']})
  tasks.push({exchange:1, ret:'depth', param:['GetDepth']})
  tasks.push({exchange:0, ret:'sellID', param:['Buy', Info[0].depth.Asks[0].Price, 10]})
  tasks.push({exchange:1, ret:'buyID', param:['Sell', Info[1].depth.Bids[0].Price, 10]})
}
function worker(){
    var jobs = []
    for(var i=0;i<tasks.length;i++){
        var task = tasks[i]
        jobs.push(G(exchanges[task.exchange].Go.apply(this, task.param), task, function(v, task) {
                    Info[task.exchange][task.ret] = v // Here "v" is the return value of the concurrent Go function "wait()", and you can think about it 
                }))
    }
    _.each(jobs, function(t){
            t.run() // Here all tasks are executed concurrently 
        })
    tasks = []
}
function main() {
    while(true){
        produce()         // Give trading command
        worker()        // Concurrently execute
        Sleep(1000)
    }
}

Es scheint, dass nur eine einfache Funktion in den oben genannten Operationen implementiert wurde. Tatsächlich hat das die Komplexität des Codes erheblich vereinfacht. Wir müssen uns nur darum kümmern, welche Aufgaben das Programm erzeugen muss, und das worker() Programm wird sie automatisch gleichzeitig ausführen und die entsprechenden Ergebnisse zurückgeben. Die Flexibilität hat sich stark verbessert.

Zeichnen nach Diagrammfunktion

In dem Grundlehrbuch empfiehlt man bei der Einführung der Zeichnung die Zeichnungsklasse-Bibliothek, die in den meisten Fällen den Bedürfnissen gerecht werden kann.

Die internen Parameter vonChart({…})sind die Objekte von HighStock und HighCharts, aber ein zusätzlicher Parameter__isStockDie Funktionsweise von HighStock basiert auf den Grundmodulen von HighCharts und HighStock, unterstützt jedoch keine zusätzlichen Module.

Das spezifische HighCharts-Beispiel:https://www.highcharts.com/demoHighStock Beispiel:https://www.highcharts.com/stock/demoSie können sich auf die Codes in diesen Beispielen beziehen und sie bequem auf FMZ transplantieren.

Sie können add ([series index ((wie 0), data]) aufrufen, um Daten in die Reihe mit angegebenem Index hinzuzufügen. Call reset (() aufrufen, um die Diagrammdaten zu klären; reset kann einen Zahlenparameter nehmen und den zu speichernden Betrag angeben. Mehrfache Diagrammdarstellung wird unterstützt, die nur in den Arrayparametern während der Konfiguration übergeben werden muss, z. B.: var chart = Chart (([{...}, {...}, {...})). Zum Beispiel, wenn Chart1 zwei Reihen hat, Chart2 eine Reihe hat und Chart3 eine Reihe hat, werden beim Aufruf add, Serie ID 0 und 1 angegeben, um die Daten der beiden Reihen in der aktualisierten Chart1 separat darzustellen; die Serie ID 2 wird angegeben, um die Daten der ersten Reihe in Chart2 darzustellen; die Serie ID 3 wird angegeben, um die Daten der ersten Reihe in Chart3 darzustellen.

Ein konkretes Beispiel:

var chart = { // This "chart" in JS is an object; before using the Chart function, we need to declare the object variable of a configured chart "chart" 
    __isStock: true,                                    // Mark whether it is a general chart; you can change it to false and try to operate it, if you are interested 
    tooltip: {xDateFormat: '%Y-%m-%d %H:%M:%S, %A'},    // Zoom tool
    title : { text : 'spread chart'},                       // Theme
    rangeSelector: {                                    // Choose the range
        buttons:  [{type: 'hour',count: 1, text: '1h'}, {type: 'hour',count: 3, text: '3h'}, {type: 'hour', count: 8, text: '8h'}, {type: 'all',text: 'All'}],
        selected: 0,
        inputEnabled: false
    },
    xAxis: { type: 'datetime'},                         // Horizontal axis, namely X axis; currently set type: time
    yAxis : {                                           // Vertical axis, namely Y axis; the default changes according to the data 
        title: {text: 'spread'},                           // Theme
        opposite: false,                                // whether to enable the vertical axis on the right 
    },
    series : [                                          // Data series; the attribute saves all kinds of data series (lines, K-lines, labels, etc.)
        {name : "line1", id : "line1,buy1Price", data : []},  // The index is 0; the data stroed in the data array is the data of the index series 
        {name : "line2", id : "line2,lastPrice", dashStyle : 'shortdash', data : []}, // The index is 1; set  dashStyle: 'shortdash', namely: set  dashed line
    ]
};
function main(){
    var ObjChart = Chart(chart);  // Call the Chart function, and initialize the chart 
    ObjChart.reset();             // Empty 
    while(true){
        var nowTime = new Date().getTime();   // Obtain the timestamp of this polling, namely a millisecond tiemstamp, to ensure the location of writing to the X axis in the chart 
        var ticker = _C(exchange.GetTicker);  // Obtain the market quotes data
        var buy1Price = ticker.Buy;           // Get buy one price from the return value of the market quotes 
        var lastPrice = ticker.Last + 1;      // Get the final executed price, and we add 1 to split the 2 lines 
        ObjChart.add([0, [nowTime, buy1Price]]); // Use the timestamp as the value of X, and buy one price as the value of Y; pass in the data series of index 0  
        ObjChart.add([1, [nowTime, lastPrice]]); // Same as above. 
        Sleep(2000);
    }
}

Beispiel für die Verwendung des Diagrammlayouts:https://www.fmz.com/strategy/136056

Erweiterter Backtest

Python-Lokal-Backtest

Spezifische Open Source-Adresse:https://github.com/fmzquant/backtest_python

Einrichtung

Geben Sie den folgenden Befehl in die Befehlszeile ein:

pip install https://github.com/fmzquant/backtest_python/archive/master.zip

Ein einfaches Beispiel

Setzen Sie die Backtestparameter zu Beginn des Strategiecodes in Form einer Bemerkung, und die Details werden unter der Schaltfläche "Einstellungen speichern" auf der Seite "Strategie bearbeiten" der FMZ-Website angezeigt.

'''backtest
start: 2018-02-19 00:00:00
end: 2018-03-22 12:00:00
period: 15m
exchanges: [{"eid":"OKEX","currency":"LTC_BTC","balance":3,"stocks":0}]
'''
from fmz import *
task = VCtx(__doc__) # initialize backtest engine from __doc__
print exchange.GetAccount()
print exchange.GetTicker()
print task.Join() # print backtest result

Zurückprüfung

Für vollständige Strategien benötigen unendliche Schleifen, der EOF-Fehler wird nach Abschluss des Backtests angehoben; daher sollten wir die Fehlerverträglichkeit gut machen.

# !/usr/local/bin/python
# -*- coding: UTF-8 -*-

'''backtest
start: 2018-02-19 00:00:00
end: 2018-03-22 12:00:00
period: 15m
exchanges: [{"eid":"Bitfinex","currency":"BTC_USD","balance":10000,"stocks":3}]
'''

from fmz import *
import math
import talib

task = VCtx(__doc__) # initialize backtest engine from __doc__

# ------------------------------ Start of the Strategy  --------------------------

print exchange.GetAccount()     # Call some interfaces, and print their return values 
print exchange.GetTicker()

def adjustFloat(v):             # the custom functions in the strategy 
    v = math.floor(v * 1000)
    return v / 1000

def onTick():
    Log("onTick")
    # Specific strategy code 


def main():
    InitAccount = GetAccount()
    while True:
        onTick()
        Sleep(1000)

# ------------------------------ End of the Strategy --------------------------

try:
    main()                     # The end of the backtest will raise EOFError() to stop stop the backtest loop. Therefore, we should handle with the error, and call task.Join() to print the backtest result, after the error is detected  
except:
    print task.Join()         

Benutzerdefinierte Backtestdaten

exchange.SetData(arr) schaltet die Backtest-Datenquelle und verwendet benutzerdefinierte K-Liniendaten. Der Parameter arr ist ein Array, dessen Elemente K-Linienbalkendaten sind (d. h.: K-Liniendatenarray, das vorübergehend nur JavaScript-Backtest unterstützt).

im Array von arr das Datenformat eines einzelnen Elements:

[
    1530460800,    // time     Timestamp 
    2841.5795,     // open     Open Price 
    2845.6801,     // high     Highest Price
    2756.815,      // low      Lowest Price
    2775.557,      // close    Close Price
    137035034      // volume   Executed Volume 
]

Die Datenquelle kann in die Vorlage importiert werden.

function init() {                                                          // The init function in the template will be executed first when the template is loaded; ensure the exchange.SetData(arr) function is executed first, initialized, and set the data to the backtest system
    var arr = [                                                            // The K-line data to be used during backtest
        [1530460800,2841.5795,2845.6801,2756.815,2775.557,137035034],      // The data of the earliest K-line bar
        ... ,                                                              // If the K-line data is too long, use "..." to represent the omitted data here
        [1542556800,2681.8988,2703.5116,2674.1781,2703.5116,231662827]     // The data of the latest K-line bar 
    ]
    exchange.SetData(arr)                                                  // Import the custom data mentioned above 
    Log("Import data successfully")
}

Hinweis: Stellen Sie sicher, dass Sie die benutzerdefinierten Daten zuerst während der Initialisierung importieren (d. h. die Exchange.SetData-Funktion aufrufen, um die Daten festzulegen). Die benutzerdefinierte K-Liniendatenperiode muss mit der auf der Backtestseite festgelegten Unterlagen-K-Linienperiode übereinstimmen, d. h.: benutzerdefinierte K-Liniendaten; die Zeit einer K-Line beträgt 1 Minute, daher sollte auch die Periode der im Backtest festgelegten Unterlagen-K-Line auf 1 Minute festgelegt werden.

Nutzung von nicht von FMZ unterstützten Plattformen

Wenn die API einer nicht unterstützten Plattform genau die gleiche ist wie die einer unterstützten Plattform, mit Ausnahme der Basisadresse, kann die nicht unterstützte Plattform durch Wechseln der Basisadresse unterstützt werden.

exchange.IO("base", "http://api.huobi.pro") 
//http://api.huobi.pro is the base address of the unsupported platform API, and notice not to add api/v3 and so on, for the address will be automatically completed

Nicht alle Plattformen werden von FMZ unterstützt, aber unsere Plattform hat die Zugriffsmethode des allgemeinen Protokolls bereitgestellt.

  • Wenn Sie den Code schreiben, um auf eine Plattform zuzugreifen, erstellt das Programm einen Webservice.
  • Wenn Sie eine Plattform zu FMZ hinzufügen, geben Sie die Adresse und den Port des Webdienstes an.
  • Wenn der Docker den Plattformbot des allgemeinen Protokolls ausführt, wird die API-Zugriffsanfrage in der Strategie an das allgemeine Protokoll gesendet.
  • Das allgemeine Protokoll greift per Anfrage auf die Plattform zu und gibt das Ergebnis an den Docker zurück.

Einfach ausgedrückt, ist das allgemeine Protokoll wie ein Vermittler, der die Anfrage des Dockers proxiert und die Daten gemäß dem entsprechenden Standard zurückgibt. Der Code des allgemeinen Protokolls muss selbst ausgefüllt werden. Das Schreiben des allgemeinen Protokolls bedeutet eigentlich, dass Sie ausschließlich auf die Plattform zugreifen und die Strategie abschließen können. FMZ offiziell veröffentlicht manchmal die exe-Version des allgemeinen Protokolls der Plattformen. Das allgemeine Protokoll kann auch in Python durchgeführt werden, das dann als gewöhnlicher Bot auf dem Docker ausgeführt werden kann.

Besondere Einführung des allgemeinen Protokolls:https://www.fmz.com/bbs-topic/9120Beispiel für das Schreiben des allgemeinen Protokolls in Python:https://www.fmz.com/strategy/101399

Erstellen Sie Ihre eigene Quantitative Plattform

So wie verschiedene Operationen einer Plattform über die API implementiert werden können, basiert auch die FMZ-Website auf der API. Sie können Ihren eigenen API-Key der FMZ-Website beantragen, um Funktionen wie create, restart, DeleteRobot, GetRobotList GetRobotLogs usw. zu realisieren. Bitte beachten Sie den Abschnitt API-Erweiterung der FMZ-Plattform im API-Dokument.

Aufgrund der leistungsstarken Erweiterbarkeit der FMZ Quant-Plattform können Sie Ihre eigene quantitative Plattform auf Basis der API-Erweiterung erstellen, so dass Ihre Benutzer Bots auf Ihrer Plattform ausführen können usw. Spezifische Referenz:https://www.fmz.com/bbs-topic/1697 .

Partner der FMZ werden

Förderung von NetEase Classroom

Der Kryptowährungshandelsmarkt hat aufgrund seiner Besonderheit immer mehr Aufmerksamkeit von quantitativen Händlern auf sich gezogen. Tatsächlich ist der Programmhandel zum Mainstream der Kryptowährung geworden, und Strategien wie Hedging und Market Making sind immer aktiv auf dem Markt. Anfänger mit schwachen Programmiergrundlagen wollen in dieses neue Feld einsteigen, konfrontiert mit zahlreichen Plattformen und sich ändernden APIs, voller Schwierigkeiten.www.fmz.com) ist derzeit die größte Quantitative Community und Plattform für Kryptowährungen und hat seit mehr als 4 Jahren Tausenden von Anfängern auf dem Weg zum quantitativen Handel geholfen.

Förderung vonQuantitative Cryptocurrency Trading-Kurs im NetEase Cloud Classroom. Melden Sie sich bei NetEase Cloud Classroom an und teilen Sie Ihren Kurslink (der Link hat eine eindeutige Kurs-Id). Andere, die sich über diesen Link registrieren und den Kurs kaufen, bringen Ihnen 50% des Gesamtbetrags als Provision, nämlich 10 Yuan. Folgen Sie dem WeChat-Public-Account von NetEase Cloud Classroom Premium Course Promotion, um das Geld abzuheben. Sie können auch andere einladen, den Kurs in Weibo oder QQ-Gruppe zu fördern.

Verwandtschaft

Verbraucher, die auf den Promotionslink klicken, sich innerhalb von einem halben Jahr registrieren und aufladen, werden die Richtlinie genießen, dass unsere Firma den effektiven Betrag in der gültigen Reihenfolge zurückgibt. Die Provision wird in Form von Punkten auf das Konto des Promotors zurückgezahlt. Die Benutzer können die Punkte in einem Verhältnis von 10:1 in den Kontostand der FMZ-Plattform umtauschen und die Benutzer können die Punkte auch für den Austausch der zugehörigen Produkte von FMZ Quant in Zukunft verwenden.https://www.fmz.com/bbs-topic/3828

FMZ Quant-Plattform für Unternehmen

Die vollständige FMZ-Website kann auf dem exklusiven Server eines Unternehmens oder eines Teams für vollständige Steuerung und funktionale Anpassung bereitgestellt werden. Die FMZ-Website wurde von etwa 100.000 Benutzern verwendet und getestet und hat eine hohe Verfügbarkeit und Sicherheit erreicht, die Zeit für quantitative Teams und Unternehmen sparen kann. Die Enterprise-Version ist für mittelständische quantitative Handelsteams, Rohstoff-Futures-Dienstleister usw. Bitte kontaktieren Sie den Administrator für spezifische Angebote.

Marktgestaltungssystem

Das professionelle System, das die Marktliquidität und das Fondsmanagement für Plattformen bereitstellt, ist möglicherweise das am besten entwickelte Market-Making-System auf dem Markt.

Plattformsystem

Das FMZ-Technologie-Handelssystem setzt die Speicher-Matching-Technologie ein und die Auftragsverarbeitungsgeschwindigkeit beträgt bis zu 2 Millionen Transaktionen pro Sekunde, was sicherstellen kann, dass es keine Verzögerung oder Verzögerung bei der Auftragsverarbeitung gibt. Es kann den reibungslosen und stabilen Betrieb von Plattformen mit mehr als 20 Millionen gleichzeitigen Online-Nutzern gewährleisten. Der Multi-Layer- und Multi-Cluster-Systemrahmen sorgt für Sicherheit, Stabilität und Erweiterbarkeit des Systems. Funktionsbereitstellung und Versionaktualisierungen können ohne Ausfallzeiten durchgeführt werden, was die Betriebserfahrung der Terminalbenutzer maximal garantiert. Derzeit kann das System in der simulierten Plattform wex.app erlebt werden.


Mehr