[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.
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.Info
Das Feld gibt Rohinformationen zurück, aber es macht immer noch keinen Unterschied für das Problem der Nichtunterstützung von Schnittstellen.
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)
}
Um auf öffentliche Schnittstellen zuzugreifen, kann JsHttpQuery
, und Python kann verwandte Pakete verwenden, wieurllib
oderrequests
.
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()
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:
https://www.bitmex.com/api/explorer/
;https://www.bitmex.com/api/v1/order
, mit der MethodePOST
; wenn FMZ die Basisadresse bereits intern angegeben hat, müssen Sie nur 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
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.
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;
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"]}')
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)
}
}
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
In diesem Fall ist es offensichtlich, dass die einfache Verwendung von
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
}
}
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
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)
}
}
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.
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
, GetAccount
und so weiter.IO
wird 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
Zu diesem Zeitpunkt kann der Gesamtprogrammrahmen als Modell geschrieben werden, ähnlich dem
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
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__isStock
Die 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
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()
exchange.SetData(arr) schaltet die Backtest-Datenquelle und verwendet benutzerdefinierte K-Liniendaten. Der Parameter
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
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.
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.
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
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
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 .
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
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
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.
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.
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.