[TOC]
Dieses Tutorial wird mehr Details über die FMZ-Plattform, mehr praktische Fähigkeiten zur Verwendung von API abdecken.
Nachdem Sie das gesamte Tutorial gelernt haben, werden Sie FMZ voll ausnutzen und in der Lage sein, maßgeschneiderte, effizientere und komplexere Strategien zu schreiben.
Sie können auf mehreren Börsen und mehreren Symbolen innerhalb eines Roboter leicht handeln.
exchange.GetTicker()
wenn eine Umtauschstelle hinzugefügt wirdexchanges[0].GetTicker()
, exchanges[1].GetTicker()
exchange
durch VerwendungIO
Funktionvar symbols = ["BTC_USDT", "LTC_USDT", "EOS_USDT", "ETH_USDT", "BCC_USDT"]
var buyValue = 1000
function main(){
for(var i=0;i<symbols.length;i++){
exchange.IO("currency", symbols[i]) // It is always valid until the next change
var ticker = exchange.GetTicker()
var amount = _N(buyValue/ticker.Sell, 3)
exchange.Buy(ticker.Sell, amount)
Sleep(1000)
}
}
Bisher unterstützt FMZ alle großen Futures-Börsen wie OKEX, HuobiDM, BitMEX, GateIO und Deribit sowie deren Swap-Kontrakte.
Um Futures auf FMZ zu handeln, müssen Sie zuerst eine Futures-Börse hinzufügen, das Symbol beim Starten des Bots einstellen und den Vertragstyp in Ihrem Code einstellen.
Wenn eine Börse sowohl Spot als auch Futures unterstützt, sollten sie dem FMZ separat hinzugefügt werden.
Das Bild unten zeigt, wie man das Futures-Symbol auf BTC setzt, wenn man den Bot startet.
Im Folgenden wird beschrieben, wie Sie einen Vertragstyp für jede Börse festlegen.
exchange.SetContractType("swap")
exchange.SetContractType("this_week")
exchange.SetContractType("next_week")
exchange.SetContractType("quarter")
exchange.SetContractType("this_week")
exchange.SetContractType("next_week")
exchange.SetContractType("quarter")
exchange.SetContractType("XBTUSD")
exchange.SetContractType("XBTM19")
exchange.SetContractType("swap")
exchange.SetContractType("BTC-PERPETUAL")
exchange.SetContractType("BTC-27APR18")
Grundlegende Einführung
FMZ verfügt über zwei Backtestmodi:real tick
undsimulate tick
. Die tatsächliche Tick-Ebene enthält die gesamten vollendeten historischen Daten (ein Tick pro Sekunde), so dass die Rückprüfungsergebnisse zuverlässiger sind. Die Simulationsstufe verwendet die Daten der Historie Klines im Intervall, das von Ihrer Strategie verwendet wird. Die Tick-Ebenen innerhalb einer Kline werden durch einen Algorithmus erzeugt, der mit MT4 identisch ist.https://www.mql5.com/en/articles/75In der Zwischenzeit kann ein kürzeres Intervall als Basis-Kline gewählt werden, um Zecken zu erzeugen.
Ein kürzerer Kline-Intervall im Simulations-Tick-Modus ist ein Kompromiss zwischen Genauigkeit und Testgeschwindigkeit.
Backtestkonfiguration
Hier sind die Standardeinstellungen:Verborgene Steche:
Backtest-Ergebnis
Wenn Funktionen aufgerufen werden, die auf die Exchange-API zugreifen (z. B.GetTicker
, Buy
, CancelOrder
, etc...), können Sie aufgrund eines Exchange-Serverproblems, falscher Parameter, Netzwerkübertragungsproblems usw. einen Zugriffsfehler erhalten.null
Sie müssen also wissen, wie man mit Fehlern umgeht.
Wo liegt der Fehler?
Der Bot wird eine Fehlermeldung zurückgeben, wenn ein Fehler auftritt.{"result":false,"error_code":20049}
wird beim Anruf zurückgegebenexchange.GetAccount()
auf OKEX.OKEX 20049
Hier ist das Ergebnis:Sie können auch den Fehlercode auf Exchange API Doc überprüfen, wieOKEX Fehlercode für Futures
Fehler bei der Abwicklung
Sie sollten sich überlegen, wie Sie mit Fehlern umgehen, wenn Sie den Strategiecode schreiben.
// 1.Deal when the result is null
var ticker = exchange.GetTicker()
while(ticker == null){
Log('GetTicker error')
Sleep(100)
ticker = exchange.GetTicker()
}
Log(ticker.Last);
// 2.Refer when the result is not null
var ticker = exchange.GetTicker()
if(!ticker){
Log(ticker.Last)
}
// 3._C() fucntion retry
var ticker = _C(exchange.GetTicker) // can't apply _C to CancelOrder, Why?
Log(ticker.Last)
// 4. try catch
try{
var ticker = exchange.GetTicker()
Log(ticker.Last)
}
catch(err){
Log('GetTicker error: ', err)
Log(GetLastError()) //literal means, get last error
}
FMZ wickelt alle verschiedenen Austauschdaten in ein und dasselbe Format, was es einfacher macht, eine plattformübergreifende Strategie zu schreiben.
GetRawJSON
Gib den ursprünglichen Inhalt (String) zurück, der durch die letzte REST-API-Anfrage zurückgegeben wurde, die verwendet werden kann, um die Rohinformationen 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
Finden Sie alle Details überHttpQuery
aufSie werden von den zuständigen Behörden des Mitgliedstaats, in dem sie sich befinden, angezeigt.
HttpQuery
gibt die Rohdaten dieser Anfrage zurück, die zuerst analysiert werden sollten.
//FMZ doesn't have a standard function for exchangeInfo that return the trading-information about all symbols.
var exchangeInfo = JSON.parse(HttpQuery('https://api.binance.com/api/v1/exchangeInfo'))
Log(exchangeInfo) // FMZ doesn't have a standard function for this API
var ticker = JSON.parse(HttpQuery('https://api.binance.com/api/v1/ticker/24hr'))
Log(ticker)
Für diese öffentlichen APIs,HttpQuery
Das ist eine wirklich nützliche Funktion.HttpQuery
unterstützt nur JavaScript, für Python, mit derurlib2
oderrequest
Sie haben die Möglichkeit, die Web-Library zu verwenden, um HTTP-Anfragen direkt zu senden.
Zwischenzeit
Für diese privaten APIs, mitHttpQuery
wird sehr kompliziert sein, weil Sie mit API-Schlüssel, Zeichen, Hash, usw. umgehen müssen.IO
ist eine praktische Funktion für diesen Zustand, überprüfen Sie es aufSiehe auch Abschnitt 6.2.3.. IO
In diesem Teil konzentrieren wir uns nur auf den Zugriff auf private APIs.
Die Verwendung dieser Funktion erfordert zunächst das Verständnis der ursprünglichen API der Börse. Im Folgenden finden Sie die Schritte, um eine Stop-Order zu erstellen, die von FMZ auf BitMEX nicht unterstützt wird.
POST
, Die Parameter umfassen Symbol,Seite, OrderQty,stopPx,ordType, die wie "Symbol=XBTUSD&side=Buy&orderQty=1&stopPx=4000&ordType=Stop" angeordnet werden sollteDer endgültige JavaScript-Code:
var id = exchange.IO("api", "POST", "/api/v1/order", "symbol=XBTUSD&side=Buy&orderQty=1&stopPx=4000&ordType=Stop")
Grundsätzlich unterstützen alle digitalen Währungsaustauschstellen das Senden von Marktdaten über Websocket, und einige Börsen unterstützen sogar das Aktualisieren von Kontoinformationen. Verglichen mit der Rest-API hat Websocket im Allgemeinen die Vorteile geringer Latenzzeit, hoher Frequenz und ist nicht durch die Anforderungsfrequenz der Plattform Rest-API begrenzt. Der Nachteil ist, dass er unterbrochen werden kann und die Verarbeitung nicht intuitiv ist.
Für JavaScript können SieDial
Funktion, um sich mit Websocket zu verbinden. Für Python können SieDial
oderwebsocket_client
libray.
Dieses Tutorial konzentriert sich auf die Verbindung von Websockets mit dem JavaScript undDial
Die Funktion Dial wurde mehrmals aktualisiert, um die verschiedenen Anwendungen zu erweitern. Dieses Tutorial zeigt die websocketbasierte Ereignis-gesteuerte Strategie und wie man sich mit mehreren Börsen verbindet.
Verbunden mit Websocket
var client = Dial("wss://stream.binance.com:9443/ws/!ticker@arr")
compress
bedeutet, dass die Daten in komprimiertem Format sind, und der Parametermode
stellt dar, ob das Senden oder Empfangen komprimiert ist. Ein Beispiel für die Verbindung mit OKEXvar client = Dial("wss://real.okex.com:10441/websocket?compress=true|compress=gzip_raw&mode=recv")
var client = Dial("wss://stream.binance.com:9443/ws/!ticker@arr?reconnect=true")
var client = Dial("wss://ws-feed.pro.coinbase.com", 60)
client.write('{"type": "subscribe","product_ids": ["BTC-USD"],"channels": ["ticker","heartbeat"]}')
Daten empfangen
Im Allgemeinen können die Daten von Websocket kontinuierlich ohne Schlaf 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() //receve data from client
var data = JSON.parse(msg) //change raw string to js object
// do something, don't need sleep.
}
}
Der zugrunde liegende Docker speichert alle Daten in der Warteschlange und gibt dann die ersten zurück, wenn das Programm aufruftread
. Die Netzwerkoperationen des Roboters wieBuy
,GetAccount
,CancelOrder
Die Daten, die wir in den letzten Jahren erhoben haben, sind in der Regel nur auf die neuesten Daten angewiesen.
Dieread()
Funktion gibt die ältesten Daten in der Warteschlange zurück, wenn keine Argumente vorhanden sind, und blockiert, wenn keine Daten vorhanden sind (das Programm wird hier pausiert).read(-2)
die neuesten Daten unverzüglich zurückzugeben undnull
Wenn keine Daten in der Warteschlange sind, wird das Programm nicht pausiert.
Verbindung mit mehreren Websockets
In diesem Fall ist es offensichtlich, dass das Programm einfach nicht verwenden kannread()
Der Grund dafür ist, dass eine Börse blockiert und auf neue Daten wartet, und eine andere Börse ihre eigenen neuen Daten nicht sofort empfängt.
function main() {
var binance = Dial("wss://stream.binance.com:9443/ws/!ticker@arr")
var coinbase = Dial("wss://ws-feed.pro.coinbase.com")
coinbase.write('{"type": "subscribe","product_ids": ["BTC-USD"],"channels": ["ticker","heartbeat"]}')
while (true) {
var msgBinance = binance.read(-1)
var msgCoinbase = coinbase.read(-1)
if(msgBinance){
// Binance has new data
}
if(msgCoinbase){
// coinbase has new data
}
Sleep(1) // just sleep 1ms
}
}
Allgemeiner Rahmen für die Nutzung von Websockets
Da die Push-Daten bereits verwendet wurden, wird das Programm natürlich als ereignisgesteuerter Typ geschrieben, wobei auf die API-Anforderungsfrequenz geachtet wird.
var tradeTime = Date.now()
var accountTime = Date.now()
function trade(data){
if(Date.now() - tradeTime > 2000){//only trade once within 2s
tradeTime = Date.now()
//trading code
}
}
function GetAccount(){
if(Date.now() - accountTime > 5000){//only get account once within 5s
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)
}
}
Alle Prameter
Die Parameter derDial(Address, Timeout)
:
Timeout
: Ausfallzeit der Verbindung
Die Adresse kann von anderen Parametern, die mit&
. Adresse und Parameter werden durch|
,
Parameter | Beschreibung |
---|---|
Kompressen | Komprimierungsmethode, kanngzip_raw , gzip . OKEX verwendetgzip_raw |
Modus | kann seindual bedeutet, dass sowohl der Sende als auch der Empfang komprimiert werden müssen,send bedeutet, dass sie komprimiert werden müssen undrecv bedeutet empfangen. |
Stellvertreter | Proxy-Einstellungen für ss5.socks5://name:pwd@192.168.0.1:1080 |
Wiederanschluss | Reconnect=true um eine Wiederaufnahme zu ermöglichen |
Abstand | interval ist das Wiederversuchsintervall, Standard ist 1000ms |
Nutzlast | Die Abonnementnachricht, die gesendet werden muss, wenn wss wieder verbunden wird |
Die Parameter derread()
Die Kommission:
Als die Websocket-Verbindung abgerissen wurde,read()
wird eine leere Zeichenfolge zurückgeben.
Parameter | Keine | -1 | -2 | 2000 |
---|---|---|---|---|
Schlange ist nicht leer | Rückgabe der ältesten Daten sofort | Rückgabe der ältesten Daten sofort | Rückgabe der letzten Daten sofort | Rückgabe der ältesten Daten sofort |
Schlange ist leer | Blockieren bis neue Daten zurück | Rückkehrnull sofort |
Rückkehrnull sofort |
Warten Sie weniger als 2000 ms, bis neue Daten zurück, andernfalls zurücknull |
Die Anwendung vonclose()
Die Kommission
Schließen Sie die Websocket-Verbindung.
function main() {
var client = Dial("wss://stream.binance.com:9443/ws/!ticker@arr|reconnect=true")
client.close()
}
Sie haben vielleicht bemerkt, dass alle Codes, die wir jetzt haben, einzelner Thread, sequentielle Ausführung sind.GO
Das ist sehr begrenzt.Go
wenn man sich wirklich um Verzögerungen und den Zeitverbrauch jeder API-Anfrage kümmert.
Auswechselung.Go (Methode, Args)
Methode: ein Funktionsname. Die Argumente der Methode.
Liste der unterstützten FunktionenGetTicker
, GetDepth
, GetTrades
, GetRecords
, GetAccount
, GetOrders
, GetOrder
, CancelOrder
, Buy
, Sell
, GetPosition
.
Ein Beispiel für JavaScript:
function main(){
var a = exchange.Go("GetTicker"); //GetTicker Asynchronous multithreaded execution
var b = exchange.Go("GetDepth");
var c = exchange.Go("Buy", 1000, 0.1);
var d = exchange.Go("GetRecords", PERIOD_H1);
// The above four operations are concurrent multi-threaded asynchronous execution, will not block and immediately return
var ticker = a.wait(); // Call wait method wait for return to asynchronous get ticker result
var depth = b.wait(); // Return depth, it is also possible to return null if it fails
var orderId = c.wait(1000); // Return the order number, 1 second timeout, timeout returns undefined, this object can continue to call wait until the last wait timeout
var records = d.wait(); // Wait for K-line result
var ret = d.wait(); // Here waits for an asynchronous operation that has waited and ended, returns null, and logs an error message.
}
wait()
Funktion muss nachGo
Funktion, andernfalls wird sich die Thread-Ressource bis 2000 ansammeln und einen Fehler zurückgeben.
LogStatus
und Tabellen
LogStatus wird eine Nachricht oder Tabellen auf der Statusleiste der Bots protokollieren, wird jedes Mal aktualisiert.
//Normal uses of LogStatus
LogStatus(" This is a normal status prompt")
LogStatus(" This is a red font status prompt #ff0000")
LogStatus(" This is a multi-line status message\n I'm the second line")
LogStatus kann Tabellen auf Ihrer Roboterseite protokollieren.`
Sie werden von der Kommission und der Kommission in Zusammenarbeit mit den Mitgliedstaaten und der Kommission geprüft.
var table = {type: 'table', title: ' Account information support color #ff0000', cols: ['BTC', 'ETH', 'USDT'], rows: [ ['free', 1, 2000], ['frozen', 0, 3000]]}
LogStatus('`' + JSON.stringify(table)+'`')
//Another example, information can also appear in multiple lines:
LogStatus("First line message\n" + JSON.stringify(table)+"`\n third line message")
//Log multiple tables in a group, switching by TAB:
var table1 = {type: 'table', title: ' Account information 1', cols: ['BTC', 'ETH', 'USDT'], rows: [ ['free', 1, 2000], ['frozen', 0, 3000]]}
var table2 = {type: 'table', title: ' Account information 2', cols: ['BTC', 'ETH', 'USDT'], rows: [ ['free', 1, 2000], ['frozen', 0, 3000]]}
LogStatus('`' + JSON.stringify([table1, table2])+'`')
Schaubild
Zeichnen Sie Zahlen auf der Seite für Robotern.
Chartunterstützung HighStocks und HighCharts, überprüfenhttps://www.highcharts.com/demoundhttps://www.highcharts.com/stock/demoFür weitere Beispiele.
Das Chart-Objekt hat eine__isStock
Attribut, das im Original nicht vorhanden ist__isStock
ist falsch, wird das Diagramm als HighCharts angezeigt.__isStock
ist wahr, wird das Diagramm als HighStocks angezeigt.reset()
um die Daten zu löschen.
Ein JavaScript-Beispiel für die Verwendung von Chart, um die Preise von zwei Symbolen zu ziehen:
// This chart is an object in the JS language. Before using the Chart function, we need to declare an object variable chart that configures the chart.
var chart = {
// Whether the mark is a general chart, if you are interested, you can change it to false and run it.
__isStock: true,
tooltip: {xDateFormat: '%Y-%m-%d %H:%M:%S, %A'}, // Zoom tool
title : { text : 'Spread Analysis Chart'}, // title
rangeSelector: { // Selection 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'}, // The horizontal axis of the coordinate axis is the x axis and the current setting type is :time
yAxis : { // The vertical axis of the axis is the y axis, and the default value is adjusted with the data size.
title: {text: 'Spread'}, // title
opposite: false, // Whether to enable the right vertical axis
},
series : [ // Data series, this attribute is saved for each data series (line, K-line graph, label, etc...)
{name : "line1", id : "Line 1,buy1Price", data : []}, // The index is 0, the data array is stored in the index series of data
{name : "line2", id : "Line 2,lastPrice", dashStyle : 'shortdash', data : []},
// The index is 1, dashStyle is set: 'shortdash' ie: Set the dotted line.
]
};
function main(){
var ObjChart = Chart(chart); // Call the Chart function to initialize the chart.
ObjChart.reset(); // Empty the chart
while(true){
var nowTime = new Date().getTime(); // Get the timestamp of this poll, which is a millisecond timestamp. Used to determine the position of the X axis written to the chart.
var tickerOne = _C(exchanges[0].GetTicker); // Get market data
var tickerTwo = _C(exchanges[1].GetTicker);
ObjChart.add([0, [nowTime, tickerOne.Last]]); // Use the timestamp as the X value and buy the price as the Y value to pass the index 0 data sequence.
ObjChart.add([1, [nowTime, tickerTwo.Last]]); // Same as above
ObjChart.update(chart); // Update the chart to show it.
Sleep(2000);
}
}
Unterstützt die Anzeige mehrerer Figuren, ein vollständiges Beispiel:https://www.fmz.com/strategy/136056
Template ist eine Bibliothek, die viele erweiterte Funktionen umfasst, die es einfacher machen, Ihre Strategie zu schreiben. Um eine Vorlage zu verwenden, sollten Sie zuerst die vorlage kopieren, die Sie benötigen. Nehmen Sie JavaScript Plot Bibliothek zum Beispiel, kopieren Sie es vonhttps://www.fmz.com/strategy/27293und speichern. Dann wählen Sie es auf der Strategiebearbeitungsseite aus.Die Funktionen werden nach$.
in der JavaScript-Vorlage und nachext.
in der Python-Vorlage.
function main() {
var isFirst = true
while (true) {
var records = exchange.GetRecords();
if (records && records.length > 0) {
$.PlotRecords(records, 'BTC')
if (isFirst) {
$.PlotFlag(records[records.length - 1].Time, 'Start', 'S')
isFirst = false
$.PlotHLine(records[records.length - 1].Close, 'Close')
}
}
var ticker = exchange.GetTicker()
if (ticker) {
$.PlotLine('Last', ticker.Last)
$.PlotTitle('Last ' + ticker.Last)
}
Sleep(60000)
}
}
Hier ist ein weiteres einfaches Beispiel, das eine Plotvorlage verwendet:https://www.fmz.com/strategy/121917
q25459768- Danke.
Das GrasIch arbeite an diesem Tutorial. Es wird ein paar Tage dauern, bis es fertig ist. Fühlen Sie sich frei, Fragen zu stellen.