[TOC] Bevor Sie diesen Tutorial lernen, müssen Sie etwas lernen.Einführung in die FMZ-Erfinder-QuantifizierungsplattformUndErste Schulung zur Strategie der FMZ-QuantifizierungsplattformDas ist eine sehr gute Gelegenheit, sich mit anderen zu beschäftigen.Der Anfanger-Tutorial behandelt die am häufigsten verwendeten Funktionen, aber es gibt noch viele andere Funktionen und Funktionen, die in diesem Tutorial nicht behandelt werden. Sie müssen sich die API-Dokumentation der Plattform ansehen.Wenn Sie den Tutorial durchlaufen haben, werden Sie in der Lage sein, freiere und maßgeschneidertere Strategien zu schreiben.
Die FMZ-Plattform ist für alle unterstützten Börsen verpackt. Um die Einheitlichkeit zu gewährleisten, ist die API-Unterstützung für einzelne Börsen nicht vollständig. Die Anzahl der K-Linien oder die Anfangszeit können in der Regel übertragen werden, wenn K-Linien erworben werden, während die FMZ-Plattform fest ist.Für eine offene Schnittstelle (sofern vorhanden) kannHttpQueryFür die Erfassung von Kontoinformationen ist die Benutzung von:IO。Die spezifischen Eingabeparameter sind in der entsprechenden Exchange-API-Dokumentation enthalten.InfoDas Feld gibt die ursprüngliche Information zurück, kann aber immer noch nicht das Problem lösen, dass die Schnittstelle nicht unterstützt wird.
Die Rückgabe des ursprünglichen Inhalts der letzten REST API-Anfrage (die String) kann zur Selbstanalyse der Erweiterungsinformationen verwendet werden.
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)
}
Zugriff auf die öffentliche Schnittstelle, die Js nutzen kannHttpQueryPython kann die entsprechenden Pakete selbst verwenden, z. B.urlliboderrequests。
HttpQuery ist die GET-Methode und unterstützt weitere Funktionen. Siehe API-Dokumentation.
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"))
Beispiele für Requests in Python
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 eine API-KEY-Signatur benötigen, kann die IO-Funktion verwendet werden. Der Benutzer muss sich nur um die eingegebenen Parameter kümmern, die spezifische Signatur wird von der unteren Ebene durchgeführt.
Die FMZ-Plattform unterstützt derzeit keine BitMEX-Stop-Loss-Anweisungen.
https://www.bitmex.com/api/explorer/。https://www.bitmex.com/api/v1/orderDie Methode istPOST。 Da FMZ die Root-Adresse bereits intern angegeben hat, muss nur “/api/v1/order” eingegeben werden。symbol=XBTUSD&side=Buy&orderQty=1&stopPx=4000&ordType=StopDer Code lautet:
var id = exchange.IO("api", "POST", "/api/v1/order", "symbol=XBTUSD&side=Buy&orderQty=1&stopPx=4000&ordType=Stop")
//也可以直接传入对象
var id = exchange.IO("api", "POST", "/api/v1/order", "", JSON.stringify({symbol:"XBTUSD",side:"Buy",orderQty:1,stopPx:4000,ordType:"Stop"}))
Weitere Beispiele für IOs: https://www.fmz.com/bbs-topic/3683
Grundsätzlich unterstützen alle digitalen Währungsbörsen die Websocket-Zustellung, einige Börsen unterstützen die Aktualisierung von Websocket-Kontoinformationen. Im Vergleich zur Rest-API weist Websocket im Allgemeinen eine geringe Verzögerung und hohe Frequenz auf, ist nicht an die Frequenzbeschränkungen der Plattform Rest API gebunden. Der Nachteil ist, dass es unterbrechungsprobleme gibt und nicht intuitiv verarbeitet wird.
Dieser Artikel wird sich hauptsächlich mit der Quantifizierungsplattform der FMZ-Erfinder befassen, die die Dial-Funktion verwendet, die in der Plattform eingebettet ist, um die Verbindung mit der JavaScript-Sprache herzustellen. Die Beschreibung und die Parameter sind in der Dokumentation, die Dial-Suche, die Dial-Funktion wurde mehrmals aktualisiert, um verschiedene Funktionen zu implementieren. Dieser Artikel wird dies abdecken und die auf WSS basierende Event-Driven-Strategie sowie die Frage der Verbindung mit mehreren Exchanges behandeln.
Im Allgemeinen gibt es direkte Verbindungen, wie z. B. die Übertragung von Sicherheitsticker:
var client = Dial("wss://stream.binance.com:9443/ws/!ticker@arr")
Für die Rückgabe von Daten ist das Komprimierungsformat erforderlich, das bei der Verbindung angegeben ist, und die Komprimierungsform wird durch die Komprimierung angegeben, wobei der Modus, der die Rückgabe von Daten sendet, die Komprimierung benötigt, wie bei der Verbindung OKEX:
var client = Dial("wss://real.okex.com:10441/websocket?compress=true|compress=gzip_raw&mode=recv")
Die Dial-Funktion unterstützt die Wiedereinbindung, die von der unteren Go-Sprache durchgeführt wird. Die Erkennung von Verbindungsunterbrechungen führt zu einer Wiedereinbindung, die für die Anforderung von Dateninhalten, die bereits in der URL enthalten sind, wie beispielsweise bei Binance, sehr praktisch und empfehlenswert ist. Für diejenigen, die eine bestellte Nachricht senden müssen, können Sie die Wiedereinbindung selbst pflegen.
var client = Dial("wss://stream.binance.com:9443/ws/!ticker@arr|reconnect=true")
Wenn Sie sich für die Nachrichten von wss anmelden möchten, können einige Börsen ihre Anfragen in der URL angeben, aber auch einige Kanäle, die sich selbst anmelden müssen, wie z. B. Coinbase:
client = Dial("wss://ws-feed.pro.coinbase.com", 60)
client.write('{"type": "subscribe","product_ids": ["BTC-USD"],"channels": ["ticker","heartbeat"]}')
Die Verwendung von Websocket ist üblich, aber es kann auch verwendet werden, um Bestellungen zu erhalten. Die Verschlüsselung dieser Art von Daten kann manchmal sehr langsam erfolgen, daher ist Vorsicht geboten. Da die Verschlüsselungsmethode komplexer ist, werden hier einige Beispiele angeführt.
//火币期货推送例子
var ACCESSKEYID = '你的火币账户的accesskey'
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} }") //去掉}}之间的多余空格
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}))
}
}
//币安推送例子,注意需要定时更新listenKey
var APIKEY = '你的币安accesskey'
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()
}
//BitMEX推送例子
var APIKEY = "你的Bitmex API ID"
var expires = parseInt(Date.now() / 1000) + 10
var signature = exchange.HMAC("sha256", "hex", "GET/realtime" + expires, "{{secretkey} }")//secretkey在执行时自动替换,不用填写
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)
}
}
Der Code, der in der Regel in einer toten Schleife gelesen wird, lautet:
function main() {
var client = Dial("wss://stream.binance.com:9443/ws/!ticker@arr");
while (true) {
var msg = client.read()
var data = JSON.parse(msg) //把json字符串解析为可引用的object
// 处理data数据
}
}
WSS-Daten werden schnell verschickt. Die Basis von Go speichert alle Daten in einer Warteschlange, so dass sie beim Aufruf von Read zurückkehren. Auf der Festplatte können Verzögerungen auftreten, die zu einer Anhäufung der Daten führen können. Für Transaktions-Push, Konto-Push, Tiefpunkt-Push usw. benötigen wir historische Daten.
read()Wenn keine Parameter hinzugefügt werden, werden die ältesten Daten zurückgegeben, und wenn keine Daten vorhanden sind, wird die Rückgabe blockiert.client.read(-2)Wenn die Daten nicht mehr vorhanden sind, wird null zurückgegeben.
Read hat verschiedene Parameter, je nachdem, wie man mit alten Daten in der Cache umgeht, und ob sie ohne Daten verstopft sind. Das sieht kompliziert aus, aber es macht das Programm flexibler.

In diesem Fall kann das Verfahren offensichtlich nicht mit einem einfachen Read () abgeschlossen werden, da eine Börse die Warte-Nachricht blockiert, während die andere Börse keine Nachricht empfängt, auch wenn es neue Nachrichten gibt. Die allgemeine Verarbeitung ist:
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) // 参数-1代表无数据立即返回null,不会阻塞到有数据返回
var msgCoinbase = coinbase.read(-1)
if(msgBinance){
// 此时币安有数据返回
}
if(msgCoinbase){
// 此时coinbase有数据返回
}
Sleep(1) // 可以休眠1ms
}
}
Dieser Teil der Abwicklung ist problematisch, da die Push-Daten unterbrochen werden können, oder die Push-Verzögerung sehr hoch ist. Selbst wenn der Heartbeat empfangen werden kann, bedeutet dies nicht, dass die Daten noch weitergeleitet werden. Sie können einen Ereignisintervall einrichten, der erneut verbunden wird, wenn der Intervall nicht überschritten wird.
Da Push-Daten bereits verwendet werden, müssen Programme natürlich als Ereignis-Driver geschrieben werden. Beachten Sie, dass Push-Daten häufig geschickt werden, ohne dass zu viele Anfragen zu einer Sperrung führen.
var tradeTime = Date.now()
var accountTime = Date.now()
function trade(data){
if(Date.now() - tradeTime > 2000){//这里即限制了2s内只交易一次
tradeTime = Date.now()
//交易逻辑
}
}
function GetAccount(){
if(Date.now() - accountTime > 5000){//这里即限制了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)
}
}
Die Art und Weise, wie die Websockets der einzelnen Börsen verbunden sind, die Art und Weise, wie die Daten gesendet werden, der Inhalt, der abonniert werden kann, und das Datenformat sind oft unterschiedlich, so dass die Plattform nicht verpackt ist und eine eigene Verbindung mit der Dial-Funktion erforderlich ist. Dieser Artikel deckt grundsätzlich einige grundlegende Vorsichtsmaßnahmen ab.
PS. Einige Börsen bieten zwar keine Websocket-Version an, aber wenn man tatsächlich auf die Webseite geht, um die Formatierung zu nutzen, wird man feststellen, dass Websocket-Push verwendet wird. Wenn man die Abonnementsformate und die Rücksendeformate untersucht, wird man feststellen, dass einige von ihnen verschlüsselt aussehen und mit Base64 entschlüsselt und entschlüsselt werden können.
JavaScript kann parallelisiert werden über die Go-Funktion, Python kann die entsprechende Multi-Thread-Bibliothek verwenden.
Bei der Implementierung einer Quantifizierungsstrategie kann in vielen Fällen die Verzögerungseffizienz durch gleichzeitige Ausführung verringert werden. Zum Beispiel bei einer Hedging-Strategie-Festplatte, die zwei Münzen Tiefe benötigt, wird der Code in der folgenden Reihenfolge ausgeführt:
var depthA = exchanges[0].GetDepth()
var depthB = exchanges[1].GetDepth()
Es gibt eine Verzögerung bei der Anforderung einer Rest-API. Angenommen, sie beträgt 100 ms. Dann ist die Zeit, um die Tiefe zweimal zu erhalten, tatsächlich unterschiedlich. Wenn mehr Zugriffe erforderlich sind, wird das Verzögerungsproblem deutlicher und beeinträchtigt die Ausführung der Strategie.
Da JavaScript keine Mehrfachthreads enthält, wird dieses Problem durch die unterstehende Go-Funktion gelöst. Die Go-Funktion kann für APIs verwendet werden, die Netzwerkzugriff benötigen, z. B.GetDepth,GetAccountUnd so weiter.IODas ist eine Frage der Zeit.exchange.Go("IO", "api", "POST", "/api/v1/contract_batchorder", "orders_data=" + JSON.stringify(orders))Das Projekt wurde von einer Gruppe von Experten durchgeführt, die sich mit der Entwicklung von neuen Technologien befasst.
var a = exchanges[0].Go("GetDepth")
var b = exchanges[1].Go("GetDepth")
var depthA = a.wait() //调用wait方法等待返回异步获取depth结果
var depthB = b.wait()
In den meisten einfachen Fällen ist es nicht falsch, die Police auf diese Weise zu verfassen. Beachten Sie jedoch, dass dieser Vorgang bei jeder Strategieschleife wiederholt werden muss und die Zwischenvariablen a und b eigentlich nur temporäre Hilfsmittel sind. Wenn wir viele gleichzeitige Aufgaben haben, müssen wir die Entsprechung zwischen a und TiefeA sowie b und TiefeB aufzeichnen. Wenn unsere gleichzeitigen Aufgaben unsicher sind, wird die Situation komplizierter. Daher möchten wir eine Funktion implementieren: Beim Schreiben von Go-Parallelität wird eine Variable gleichzeitig gebunden, und wenn das gleichzeitig ausgeführte Ergebnis zurückgegeben wird, wird das Ergebnis automatisch der Variablen zugewiesen, wodurch die Zwischenvariablen eliminiert und das Programm mehr prägnant. Die konkrete Implementierung sieht wie folgt aus:
function G(t, ctx, f) {
return {run:function(){
f(t.wait(1000), ctx)
}}
}
Wir definieren eine G-Funktion, wobei der Parameter t die auszuführende Go-Funktion, ctx der Programmkontext und f die Funktion für die spezifische Zuweisung ist. Wir werden diese Funktion in Kürze in Aktion sehen.
An diesem Punkt kann das gesamte Programmgerüst als „Produzent-Konsument“-Modell (mit einigen Unterschieden) geschrieben werden, bei dem Produzenten kontinuierlich Aufgaben erteilen und Konsumenten diese gleichzeitig ausführen. Der folgende Code dient nur zur Demonstration und betrifft nicht das Programm. Logik ausführen.
var Info = [{depth:null, account:null}, {depth:null, account:null}] //加入我们需要获取两个交易所的深度和账户,跟多的信息也可以放入,如订单Id,状态等。
var tasks = [ ] //全局的任务列表
function produce(){ //下发各种并发任务
//这里省略了任务产生的逻辑,仅为演示
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 //这里的v就是并发Go函数wait()的返回值,可以仔细体会下
}))
}
_.each(jobs, function(t){
t.run() //在这里并发执行所有任务
})
tasks = []
}
function main() {
while(true){
produce() // 发出交易指令
worker() // 并发执行
Sleep(1000)
}
}
Es scheint, dass wir nach vielen Schritten nur eine einfache Funktion implementiert haben, aber tatsächlich wurde die Komplexität des Codes erheblich vereinfacht. Wir müssen uns nur darum kümmern, welche Aufgaben das Programm generieren muss, und den Worker ( ) Programm führt sie automatisch gleichzeitig aus und gibt die entsprechenden Ergebnisse zurück. Die Flexibilität wurde erheblich verbessert.
Die Grafik ist eine empfohlene Grafik-Kategorie, die in den meisten Fällen den Bedürfnissen entspricht. Wenn weitere Anpassungen erforderlich sind, können Sie direkt mit den Chart-Objekten umgehen.
Chart({…})Die internen Parameter sind HighStock- und HighCharts-Objekte, nur ein zusätzlicher Parameter wurde hinzugefügt__isStockUm zu unterscheiden, ob HighStock. HighStock konzentriert sich mehr auf Zeitreihenkarten und wird daher häufiger verwendet. FMZ unterstützt grundsätzlich die grundlegenden Module von HighCharts und HighStock, aber keine zusätzlichen Module.
Konkrete HighCharts-Beispiele: https://www.highcharts.com/demo ;HighStock-Beispiele: https://www.highcharts.com/stock/demo 。 Der Code dieser Beispiele kann leicht auf FMZ übertragen werden。
Sie können “add” aufrufen[series Index ((wie 0), Daten]) hinzufügen, um die angegebene Index-series, Aufruf reset (() leere Grafik-Daten, reset kann mit einem numerischen Parameter, die angegebene Anzahl der reservierten Elemente. Unterstützt die Anzeige mehrerer Grafiken, die Konfiguration nur in die Array-Parameter wie: var chart = Chart (([{…}, {…}, {…}]), z.B. Graph 1 hat zwei Serien, Graph 2 eine Serie, Graph 3 eine Serie, dann bedeutet add, dass die 0 und 1 Sequenz-IDs die Daten der beiden Sequenzen in Graph 1 aktualisieren, add, dass die Sequenz-ID 2 die Daten der ersten Serie in Graph 2 ist, und dass die Sequenz 3 die Daten der ersten Serie in Graph 3 ist.
Ein konkretes Beispiel:
var chart = { // 这个 chart 在JS 语言中 是对象, 在使用Chart 函数之前我们需要声明一个配置图表的对象变量chart。
__isStock: true, // 标记是否为一般图表,有兴趣的可以改成 false 运行看看。
tooltip: {xDateFormat: '%Y-%m-%d %H:%M:%S, %A'}, // 缩放工具
title : { text : '差价分析图'}, // 标题
rangeSelector: { // 选择范围
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'}, // 坐标轴横轴 即:x轴, 当前设置的类型是 :时间
yAxis : { // 坐标轴纵轴 即:y轴, 默认数值随数据大小调整。
title: {text: '差价'}, // 标题
opposite: false, // 是否启用右边纵轴
},
series : [ // 数据系列,该属性保存的是 各个 数据系列(线, K线图, 标签等..)
{name : "line1", id : "线1,buy1Price", data : []}, // 索引为0, data 数组内存放的是该索引系列的 数据
{name : "line2", id : "线2,lastPrice", dashStyle : 'shortdash', data : []}, // 索引为1,设置了dashStyle : 'shortdash' 即:设置 虚线。
]
};
function main(){
var ObjChart = Chart(chart); // 调用 Chart 函数,初始化 图表。
ObjChart.reset(); // 清空
while(true){
var nowTime = new Date().getTime(); // 获取本次轮询的 时间戳, 即一个 毫秒 的时间戳。用来确定写入到图表的X轴的位置。
var ticker = _C(exchange.GetTicker); // 获取行情数据
var buy1Price = ticker.Buy; // 从行情数据的返回值取得 买一价
var lastPrice = ticker.Last + 1; // 取得最后成交价,为了2条线不重合在一起 ,我们加1
ObjChart.add([0, [nowTime, buy1Price]]); // 用时间戳作为X值, 买一价 作为Y值 传入 索引0 的数据序列。
ObjChart.add([1, [nowTime, lastPrice]]); // 同上。
Sleep(2000);
}
}
Ein Beispiel für die Verwendung eines Diagramms: https://www.fmz.com/strategy/136056
Die spezifische Open-Source-Adresse lautet:
Installation
Geben Sie den folgenden Befehl auf die Befehlszeile ein:
pip install https://github.com/fmzquant/backtest_python/archive/master.zip
Einfache Beispiele
Die Retrieval-Parameter werden zu Beginn des Strategiecodes als Kommentare eingestellt. Die Retrieval-Einstellungen sind in der Strategieeditor-Oberfläche der FMZ-Website zu finden.
'''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
Backtesting
Da eine vollständige Strategie einen Dead-Loop erfordert, wird nach dem Ende der Rückmeldung eine EOF-Ausnahme ausgelöst, um die Prozedur zu beenden.
# !/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__
# ------------------------------ 策略部分开始 --------------------------
print exchange.GetAccount() # 调用一些接口,打印其返回值。
print exchange.GetTicker()
def adjustFloat(v): # 策略中自定义的函数
v = math.floor(v * 1000)
return v / 1000
def onTick():
Log("onTick")
# 具体的策略代码
def main():
InitAccount = GetAccount()
while True:
onTick()
Sleep(1000)
# ------------------------------ 策略部分结束 --------------------------
try:
main() # 回测结束时会 raise EOFError() 抛出异常,来停止回测的循环。所以要对这个异常处理,在检测到抛出的异常后调用 task.Join() 打印回测结果。
except:
print task.Join()
exchange.SetData(arr), schaltet die Rückmessungsdatenquelle mit dem benutzerdefinierten K-Linien-Daten. Der Parameter arr, ein Array, dessen Elemente K-Linien-Säulen-Daten sind (((d.h. ein Array von K-Linien-Daten, das vorläufig nur JavaScript-Rückmessungen unterstützt.]]
Das Datenformat für einzelne Elemente in einer Arr-Array ist:
[
1530460800, // time 时间戳
2841.5795, // open 开盘价
2845.6801, // high 最高价
2756.815, // low 最低价
2775.557, // close 收盘价
137035034 // volume 成交量
]
Die Datenquelle kann in die Sammlung von Template-Klassenlagern importiert werden.
function init() { // 模板中的 init 初始化函数会在加载模板时,首先执行,确保 exchange.SetData(arr) 函数先执行,初始化,设置数据给回测系统。
var arr = [ // 回测的时候需要使用的K线数据
[1530460800,2841.5795,2845.6801,2756.815,2775.557,137035034], // 时间最早的一根 K线柱 数据
... , // K线数据太长,用 ... 表示,数据此处省略。
[1542556800,2681.8988,2703.5116,2674.1781,2703.5116,231662827] // 时间最近的一根 K线柱 数据
]
exchange.SetData(arr) // 导入上述 自定义的数据
Log("导入数据成功")
}
Hinweis: Beim Initialieren muss man zuerst die benutzerdefinierten Daten importieren (d.h. die exchange.SetData-Funktion wird aufgerufen, um die Daten einzustellen). Die benutzerdefinierte K-Linien-Daten-Periode muss mit der unteren K-Linien-Periode übereinstimmen, die auf der Rückmessseite eingestellt wurde, d.h. wenn die benutzerdefinierten K-Linien-Daten eine K-Linienzeit von 1 Minute haben, dann muss auch die unteren K-Linien-Periode, die in der Rückmessung eingestellt wurde, auf 1 Minute eingestellt werden.
Wenn ein nicht unterstützter Handelsplatz genau die gleiche API hat wie ein unterstützter Handelsplatz, nur dass die Basisadresse anders ist, kann er unterstützt werden, indem er die Basisadresse wechselt. Wenn Sie den unterstützten Handelsplatz auswählen, um den Handelsplatz hinzuzufügen, aber den nicht unterstützten Handelsplatz mit dem API-KEY ausfüllen, wechseln Sie die Basisadresse in der Strategie mit dem IO, wie:
exchange.IO("base", "http://api.huobi.pro")
//http://api.huobi.pro为为支持交易所API基地址,注意不用添加/api/v3之类的,会自动补全
Nicht alle Börsen unterstützen FMZ, aber die Plattform bietet Zugang zu den allgemeinen Protokollen.
Einfach ausgedrückt, ist die General Agreement eine Art Vermittler, der die Anfrage des Host vertritt und die Daten gemäß den entsprechenden Standards zurückgibt. Der Code für die General Agreement muss selbst erstellt werden, und das Schreiben der General Agreement stellt tatsächlich dar, dass Sie die Strategie für den separaten Zugriff auf die Börse abschließen können. Die FMZ veröffentlicht manchmal offizielle Versionen der General Agreement für die Börse.
Das Projekt wurde von der Organisation “Friends of the Earth” (Friends of the Earth) in Zusammenarbeit mit der Weltgesundheitsorganisation (World Health Organization) durchgeführt. Ein Beispiel für ein allgemeines Python-Protokoll: https://www.fmz.com/strategy/101399
FMZ-Website ist API-basiert. Sie können die eigene FMZ-Website API-KEY implementieren, um Funktionen wie Erstellen, Neustart, Löschen von Festplatten, Erlangen einer Festplattenliste und Erlangen einer Festplattenlogik zu erhalten.
Aufgrund der starken Skalierbarkeit der FMZ-Plattform können Sie Ihre eigene Quantifizierungsplattform basierend auf einer erweiterten API erstellen, die Benutzer auf Ihrer Plattform mit dem Laufwerk betreiben lässt.
Der Markt für digitale Währungshandel wird zunehmend von Quantifizierungshändlern wegen seiner Besonderheit betroffen. In der Tat ist programmierter Handel bereits der Mainstream der digitalen Währung, Strategien wie Hedging-Marketing sind immer aktiv. Anfänger mit schwachen Programmiergrundlagen, die in diesen Bereich einsteigen möchten, werden mit zahlreichen Börsen und variablen APIs konfrontiert.
VerbreitungKurse zur Quantifizierung von digitalen Währungen in der NetEase Cloud Classroom│ Loggen Sie auf NetEaseCloud und teilen Sie Ihren Kurslink (der Link enthält eine eindeutige CoursId), andere registrieren sich über diesen Link und kaufen den Kurs, Sie erhalten einen Anteil von 50% von insgesamt 10 Yuan. │ Beachten Sie die WeChat-Öffentlichkeitsnummer für die Werbung für die WeChat-Öffentlichkeit.
Der Verbraucher klickt auf den Werbe-Link und wird innerhalb von sechs Monaten mit einer Aufladung registriert. Die Kommission wird gemäß dem gültigen Betrag der gültigen Bestellung zurückerstattet. Die Provision wird in Form von Punkten an das Konto des Werbetreibenden zurückerstattet.
Die gesamte FMZ-Website kann auf einem exklusiven Server des Unternehmens oder des Teams bereitgestellt werden, um vollständige Kontrolle und Funktionsanpassung zu erreichen. Die FMZ-Website wurde von etwa 100.000 Benutzern verwendet und getestet und erreicht eine hohe Verfügbarkeit und Sicherheit.
Das professionelle System, das den Börsen Marktliquidität und Kapitalverwaltung bietet, ist möglicherweise das am besten entwickelte System für die Markteinführung auf dem Markt und wird von vielen Börsen und Teams verwendet.
Das Technologie-Trading-System des Erfinders nutzt die Speicher-Zusammenführungstechnologie, die Orderbearbeitung mit einer Geschwindigkeit von bis zu 2 Millionen Pfund / Sekunde gewährleistet, dass die Orderbearbeitung keine Verzögerungen und Kartons aufweist. Die gleichzeitige Online-Nutzerzahl von mehr als 20 Millionen Börsen kann einen reibungslosen und stabilen Betrieb gewährleisten. Die mehrschichtige, mehrschichtige Systemarchitektur gewährleistet die Sicherheit, Stabilität und Erweiterbarkeit des Systems.