[TOC] Sebelum Anda mempelajari tutorial ini, Anda harus belajar terlebih dahulu.Pendahuluan dari FMZ Inventors Quantification PlatformDanTutorial dasar untuk strategi platform kuantitatif FMZDia adalah seorang blogger, blogger, dan blogger yang sudah berpengalaman dalam bahasa pemrograman.Tutorial pemula membahas fungsi yang paling umum digunakan, tetapi ada banyak fungsi dan fitur yang tidak dibahas dalam tutorial ini. Anda perlu melihat dokumentasi API platform untuk memahami sendiri.Setelah Anda menyelesaikan tutorial ini, Anda akan dapat menulis strategi yang lebih bebas dan disesuaikan, dan platform FMZ hanyalah sebuah alat.
Platform FMZ dibungkus untuk semua bursa yang didukung, untuk menjaga keseragaman, dukungan API untuk bursa tunggal tidak lengkap. Sebagai contoh, untuk mendapatkan K-line umumnya dapat ditransfer ke jumlah K-line atau waktu awal, sedangkan platform FMZ tetap, beberapa platform mendukung pesanan massal, FMZ tidak mendukung, dan sebagainya.Untuk antarmuka terbuka (jika ada), dapat digunakanHttpQueryUntuk mendapatkan informasi lebih lanjut tentang akun, Anda perlu menggunakan https://www.facebook.com`IO`。Parameter input yang spesifik, juga merujuk pada dokumen API exchange yang sesuai.InfoField mengembalikan informasi asli, tetapi masih tidak dapat menyelesaikan masalah yang tidak mendukung interface.
Mengembalikan konten asli yang dikembalikan oleh permintaan REST API terakhir (string) yang dapat digunakan untuk menguraikan informasi ekstensi sendiri.
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)
}
Akses ke antarmuka publik, Js dapat digunakanHttpQueryPython sendiri dapat menggunakan paket terkait, seperti:urllibataurequests。
HttpQuery adalah metode GET secara default, dan mendukung lebih banyak fitur, lihat dokumentasi API.
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"))
Contoh penggunaan requests di 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()
Untuk antarmuka yang membutuhkan tanda tangan API-KEY, fungsi IO dapat digunakan, pengguna hanya perlu memperhatikan parameter yang ditransmisikan, dan proses tanda tangan akan dilakukan oleh lapisan bawah.
Platform FMZ saat ini tidak mendukung BitMEX Stop Loss Order, melalui IO melalui langkah-langkah berikut:
https://www.bitmex.com/api/explorer/。https://www.bitmex.com/api/v1/orderMetode adalahPOSTKarena FMZ sudah menetapkan alamat root secara internal, cukup masukkan “/api/v1/order”.symbol=XBTUSD&side=Buy&orderQty=1&stopPx=4000&ordType=StopKode spesifik:
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"}))
Lebih banyak contoh IO: https://www.fmz.com/bbs-topic/3683
Pada dasarnya, semua bursa mata uang digital mendukung pengiriman websocket, beberapa bursa mendukung informasi pembaruan akun websocket. Dibandingkan dengan rest API, websocket umumnya memiliki latensi rendah, frekuensi tinggi, tidak dibatasi oleh frekuensi rest API platform.
Artikel ini akan membahas tentang Inventor Quantum Platform di FMZ, menggunakan bahasa JavaScript, menggunakan fungsi Dial yang terbungkus dalam platform untuk melakukan koneksi, spesifikasi dan parameternya dalam dokumentasi, mencari Dial, fungsi Dial telah diperbarui beberapa kali untuk berbagai fungsi, artikel ini akan membahas ini, dan memperkenalkan strategi event-driven berbasis wss, dan masalah koneksi multi-exchange. Python juga dapat menggunakan fungsi Dial, juga dapat menggunakan perpustakaan yang sesuai.
Biasanya koneksi langsung tersedia, seperti mendapatkan ticker keamanan mata uang:
var client = Dial("wss://stream.binance.com:9443/ws/!ticker@arr")
Untuk data yang dikembalikan adalah format kompresi, perlu dihubungkan adalah ditentukan, compress menentukan format kompresi, modus mewakili mengirim data yang dikembalikan yang perlu dikompresi, seperti koneksi OKEX:
var client = Dial("wss://real.okex.com:10441/websocket?compress=true|compress=gzip_raw&mode=recv")
Fungsi dial mendukung koneksi ulang, yang dilakukan oleh bahasa Go yang mendasari, mendeteksi koneksi yang terputus dan melakukan koneksi ulang, sangat nyaman dan direkomendasikan untuk penggunaan untuk konten data yang telah diminta di url, seperti contoh Binance yang baru saja dilakukan. Untuk yang perlu mengirim pesan terdaftar, Anda dapat memelihara mekanisme koneksi ulang sendiri.
var client = Dial("wss://stream.binance.com:9443/ws/!ticker@arr|reconnect=true")
Untuk berlangganan berita wss, beberapa bursa meminta di url, dan ada juga saluran yang perlu berlangganan sendiri, seperti coinbase:
client = Dial("wss://ws-feed.pro.coinbase.com", 60)
client.write('{"type": "subscribe","product_ids": ["BTC-USD"],"channels": ["ticker","heartbeat"]}')
Biasanya menggunakan websocket untuk membaca situasi, tetapi juga dapat digunakan untuk mendapatkan pesanan, akun push, push data enkripsi semacam ini kadang-kadang memiliki banyak penundaan, perlu berhati-hati. Karena metode enkripsi lebih kompleks, beberapa contoh diberikan di sini sebagai referensi. Perhatikan bahwa hanya memerlukan AccessKey, dapat diatur sebagai parameter kebijakan, jika diperlukan SecretKey, dapat digunakan untuk panggilan implisit fungsi exchange.HMAC ((, untuk menjamin keamanan.
//火币期货推送例子
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)
}
}
Kode yang dapat dibaca secara terus menerus dalam loop mati adalah sebagai berikut:
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 data push sangat cepat, Go dasar akan menyimpan semua data dalam antrian, seperti program yang dipanggil read, kemudian kembali. Sementara operasi seperti perintah pada hard disk akan membawa penundaan, yang dapat menyebabkan akumulasi data. Untuk transaksi push, push akun, push nilai mendalam, dan lain-lain, kita perlu data historis, untuk data situasi, kita hanya peduli tentang yang terbaru, tidak peduli tentang data historis.
read()Jika tidak ditambah parameter, akan dikembalikan data tertua, jika tidak ada data diblokir untuk dikembalikan. Jika ingin data terbaru, dapat digunakanclient.read(-2)Kembali ke data terbaru, tetapi kembali ke null jika tidak ada data lagi, perlu untuk menilai referensi lagi.
Read memiliki parameter yang berbeda, tergantung pada bagaimana data lama yang di-cache diperlakukan, dan apakah data yang tidak terblokir, seperti gambar di bawah ini, terlihat rumit, tetapi membuat program lebih fleksibel.

Untuk situasi seperti ini jelas tidak dapat digunakan read () sederhana, karena satu bursa akan memblokir pesan yang sedang menunggu, sementara bursa lain tidak akan menerima pesan baru meskipun ada. Proses umum adalah:
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
}
}
Bagian pengolahan ini agak merepotkan, karena data push mungkin terganggu, atau penundaan push sangat tinggi, bahkan jika dapat menerima heartbeat tidak berarti data masih di-push, Anda dapat mengatur interval peristiwa, jika melebihi interval tidak menerima pembaruan akan terhubung kembali, dan yang terbaik adalah membandingkan hasil dengan rest kembali setelah beberapa waktu, untuk melihat apakah data akurat. Untuk situasi khusus ini, Anda dapat langsung mengatur koneksi ulang otomatis.
Karena telah menggunakan data push, program secara alami juga harus ditulis sebagai event drive, perhatikan bahwa data push sering, tanpa terlalu banyak permintaan menyebabkan pemblokiran, umumnya dapat ditulis sebagai:
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)
}
}
Cara koneksi websocket, cara pengiriman data, konten yang dapat berlangganan, dan format data seringkali berbeda di setiap bursa, sehingga platform tidak terbungkus dan perlu terhubung sendiri dengan fungsi Dial. Artikel ini mencakup beberapa hal dasar yang perlu diperhatikan, dan jika ada pertanyaan, silakan bertanya.
PS. Meskipun beberapa exchange tidak menyediakan websocket, namun sebenarnya ketika Anda masuk ke situs menggunakan fitur modding, Anda akan menemukan bahwa semua yang digunakan adalah websocket push, dan jika Anda meneliti, Anda akan menemukan format berlangganan dan format pengembalian. Beberapa terlihat seperti telah dienkripsi dan dapat didekripsi dengan base64 decoding.
JavaScript dapat diimplementasikan secara paralel melalui fungsi Go, Python dapat menggunakan perpustakaan multithread yang sesuai.
Dalam implementasi strategi kuantitatif, dalam banyak kasus, eksekusi paralel dapat mengurangi efisiensi penundaan. Sebagai contoh, dalam strategi hedging, Anda perlu mendapatkan kedalaman dua koin, dan kode yang dieksekusi berurutan adalah sebagai berikut:
var depthA = exchanges[0].GetDepth()
var depthB = exchanges[1].GetDepth()
Ada penundaan dalam meminta API rest. Asumsikan 100 ms. Maka waktu untuk mendapatkan kedalaman dua kali sebenarnya berbeda. Jika diperlukan akses lebih banyak, masalah penundaan akan lebih menonjol, yang memengaruhi pelaksanaan strategi.
Karena JavaScript tidak memiliki multi-thread, fungsi Go yang terbungkus di bawahnya mengatasi masalah ini. Fungsi Go dapat digunakan untuk API yang memerlukan akses jaringan, sepertiGetDepth,GetAccountDan lain-lain.IOPanggilan seperti:exchange.Go("IO", "api", "POST", "/api/v1/contract_batchorder", "orders_data=" + JSON.stringify(orders))Namun, karena mekanisme desain, implementasinya agak rumit.
var a = exchanges[0].Go("GetDepth")
var b = exchanges[1].Go("GetDepth")
var depthA = a.wait() //调用wait方法等待返回异步获取depth结果
var depthB = b.wait()
Dalam kebanyakan kasus sederhana, tidak ada yang salah dengan penulisan kebijakan dengan cara ini. Namun perlu dicatat bahwa proses ini harus diulang setiap kali strategi berulang, dan variabel antara a dan b sebenarnya hanya bantuan sementara. Jika kita memiliki banyak tugas yang dilakukan secara bersamaan, kita perlu mencatat korespondensi antara a dan depthA, serta b dan depthB. Ketika tugas-tugas yang dilakukan secara bersamaan tidak pasti, situasinya menjadi lebih rumit. Oleh karena itu, kami ingin menerapkan fungsi: saat menulis Go concurrency, mengikat variabel pada saat yang sama, dan ketika hasil berjalan bersamaan dikembalikan, hasilnya secara otomatis ditetapkan ke variabel, sehingga menghilangkan variabel perantara dan membuat program lebih ringkas. Implementasi spesifiknya adalah sebagai berikut:
function G(t, ctx, f) {
return {run:function(){
f(t.wait(1000), ctx)
}}
}
Kami mendefinisikan fungsi G, di mana parameter t adalah fungsi Go yang akan dieksekusi, ctx adalah konteks program, dan f adalah fungsi untuk penugasan tertentu. Kita akan segera melihat fungsi ini beraksi.
Pada titik ini, keseluruhan kerangka program dapat ditulis sebagai model “produsen-konsumen” (dengan beberapa perbedaan), di mana produsen terus-menerus mengeluarkan tugas dan konsumen menjalankannya secara bersamaan. Kode berikut hanya untuk demonstrasi dan tidak melibatkan program. Menjalankan logika.
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)
}
}
Tampaknya kita baru saja mengimplementasikan fungsi sederhana setelah melalui banyak langkah, tetapi sebenarnya, kompleksitas kode telah sangat disederhanakan. Kita hanya perlu memperhatikan tugas apa yang perlu dihasilkan oleh program, dan pekerja( ) program akan secara otomatis mengeksekusinya secara bersamaan dan mengembalikan hasil yang sesuai. Fleksibilitas telah ditingkatkan pesat.
Tutorial awal tentang Chart adalah rekomendasi dari Chart Class Library, yang dalam kebanyakan kasus dapat memenuhi kebutuhan. Jika perlu kustomisasi lebih lanjut, Chart Object dapat dioperasikan secara langsung.
Chart({…})Parameter internal adalah objek HighStock dan HighCharts, hanya menambahkan satu parameter tambahan__isStockUntuk membedakan apakah HighStock. HighStock lebih memperhatikan grafik berurutan waktu, sehingga lebih sering digunakan. FMZ mendukung modul dasar HighCharts dan HighStock, tetapi tidak mendukung modul tambahan.
Contoh HighCharts spesifik: https://www.highcharts.com/demo; Contoh HighStock: https://www.highcharts.com/stock/demo 。 Referensi kode dari contoh-contoh ini, dapat dengan mudah dipindahkan ke FMZ。
Anda dapat memanggil add ().[series index ((seperti 0), data]) menambahkan data ke series yang ditentukan indeksnya, memanggil reset ((() kosongkan data grafik, reset bisa dengan parameter digital, menentukan jumlah baris yang disimpan.[{…}, {…}, {…}]), misalnya grafik 1 memiliki dua series, grafik 2 memiliki satu series, grafik 3 memiliki satu series, maka ketika add menentukan 0 dan 1 sequence ID mewakili data dari dua sequence update dari grafik 1, ketika add menentukan sequence ID 2 mewakili data dari seri pertama grafik 2, dan menentukan seri 3 mewakili data dari seri pertama grafik 3.
Sebuah contoh konkret:
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);
}
}
Contoh penggunaan tata letak grafik: https://www.fmz.com/strategy/136056
Sumber terbuka: https://github.com/fmzquant/backtest_python
Pemasangan
Masukkan perintah berikut di baris perintah:
pip install https://github.com/fmzquant/backtest_python/archive/master.zip
Contoh sederhana
Parameter feedback diatur sebagai komentar di awal kode kebijakan, lihat pengaturan feedback yang disimpan di antarmuka pengeditan kebijakan di situs web FMZ.
'''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
Pengujian ulang
Karena strategi yang lengkap membutuhkan loop mati, pengecualian EOF akan dibuang setelah pengembalian selesai untuk mengakhiri proses, sehingga perlu melakukan toleransi kesalahan.
# !/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), beralih ke sumber data retesting, menggunakan data baris K yang disesuaikan. Parameter arr, sebuah array dengan elemen sebagai data kolom baris K ((yaitu: Array data baris K, sementara hanya mendukung retesting JavaScript。
Dalam array arr, format data untuk setiap elemen adalah:
[
1530460800, // time 时间戳
2841.5795, // open 开盘价
2845.6801, // high 最高价
2756.815, // low 最低价
2775.557, // close 收盘价
137035034 // volume 成交量
]
Sumber data dapat diimpor ke dalam Repository Template.
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("导入数据成功")
}
Catatan: Pada saat initialization, data khusus harus diimpor terlebih dahulu (misalnya, data pengaturan fungsi exchange.SetData), dan siklus data K baris harus sesuai dengan siklus K baris dasar yang ditetapkan pada halaman pengukuran ulang, yaitu: data K baris khusus, satu K baris waktu 1 menit, maka siklus K baris dasar yang ditetapkan dalam pengukuran ulang juga harus disetel menjadi 1 menit.
Jika tidak mendukung dan mendukung pertukaran API yang sama persis dengan yang didukung, hanya saja alamat dasar yang berbeda, dapat didukung dengan cara beralih alamat dasar. Secara khusus untuk menambahkan pertukaran memilih yang didukung, tetapi API-KEY mengisi tidak mendukung pertukaran, menggunakan IO dalam kebijakan beralih alamat dasar, seperti:
exchange.IO("base", "http://api.huobi.pro")
//http://api.huobi.pro为为支持交易所API基地址,注意不用添加/api/v3之类的,会自动补全
Tidak semua bursa mendukung FMZ, tetapi platform ini menyediakan akses ke protokol umum.
Secara sederhana, protokol universal setara dengan seorang perantara, yang mewakili permintaan host dan mengembalikan data sesuai standar yang sesuai. Kode protokol universal perlu diselesaikan sendiri, menulis protokol universal sebenarnya mewakili bahwa Anda dapat mengakses pertukaran secara terpisah, menyelesaikan strategi. FMZ resmi kadang-kadang merilis versi exe protokol universal untuk pertukaran. Protokol universal juga dapat diselesaikan dengan Python, yang dapat berjalan di host sebagai disk biasa.
“Saya tidak tahu apa-apa tentang apa yang terjadi”, kata dia. Contoh Python untuk menulis protokol umum: https://www.fmz.com/strategy/101399
Seperti halnya di bursa, berbagai operasi dapat dilakukan melalui API, situs FMZ juga berbasis API, Anda dapat mengajukan aplikasi sendiri untuk implementasi API-KEY situs FMZ seperti membuat, restart, menghapus disk fisik, mendapatkan daftar disk fisik, dan mendapatkan jurnal disk fisik.
Karena kemampuan FMZ yang sangat terukur, Anda dapat membuat platform kuantitatif Anda sendiri berdasarkan API ekstensi, memungkinkan pengguna menjalankan perangkat keras di platform Anda, dll.
Pasar perdagangan mata uang digital semakin menjadi perhatian para pedagang kuantitatif karena keahliannya, sebenarnya perdagangan terprogram sudah menjadi arus utama mata uang digital, strategi seperti melakukan pasar yang selalu aktif di pasar. Para pemula yang ingin memasuki bidang ini, menghadapi banyak pertukaran dan API yang bervariasi, kesulitan berat. Penemu (FMZ) platform kuantitatif (aslinya BotVs, www.fmz.com) adalah platform kuantitatif komunitas mata uang digital terbesar saat ini, selama lebih dari 4 tahun telah membantu ribuan pemula menuju perdagangan kuantitatif.
PengembanganKelas Netscape Quantitative Currency Trading❚ Login ke kelas NetEase Cloud, berbagi link kursus Anda (link dengan courseId yang unik), orang lain mendaftar dan membeli kursus melalui tautan ini, Anda akan mendapatkan 50 persen dari total 10 yuan. ❚ Perhatikan promosi belanja kelas NetEase Cloud Classroom ❚ Nomor publik WeChat tersedia.
Konsumen mengklik link promosi, dan dalam waktu enam bulan terdaftar untuk mengisi, kami akan mengembalikan komisi sesuai dengan jumlah yang berlaku dalam pesanan yang valid. Komisi akan dikembalikan ke akun promotor dalam bentuk poin, pengguna dapat menukar inventor dengan saldo akun platform perdagangan kuantitatif dengan rasio 10:1, atau dapat menukar inventor dengan poin untuk mengkuantifikasi barang-barang di sekitarnya di kemudian hari.
Situs FMZ dapat didistribusikan ke server eksklusif perusahaan atau tim, untuk mencapai kontrol penuh dan kustomisasi fungsi. Situs FMZ telah digunakan dan diuji oleh sekitar 100.000 pengguna, mencapai ketersediaan dan keamanan yang sangat tinggi, dapat menghemat biaya waktu tim dan perusahaan kuantitatif.
Sistem profesional yang menyediakan likuiditas pasar dan manajemen dana untuk bursa, mungkin merupakan sistem perdagangan terbaik di pasar, yang digunakan oleh banyak bursa dan tim.
Sistem perdagangan teknologi penemu menggunakan teknologi pencitraan memori, kecepatan pemrosesan pesanan hingga 2 juta pips / detik, dapat menjamin pemrosesan pesanan tidak akan mengalami penundaan dan ketegangan. Dapat menjaga operasi yang lancar dan stabil dari lebih dari 20 juta pengguna online secara bersamaan. Arsitektur sistem bertingkat, multi-kelompok menjamin keamanan, stabilitas, dan kemampuan untuk memperluas sistem.