[TOC] Anda perlu belajar terlebih dahulu sebelum anda boleh menggunakan tutorial ini.FMZ Inventor Quantify PlatformdanTutorial awal untuk strategi platform kuantitatif FMZIa juga boleh digunakan untuk mengedit video, video, video, dan video.Tutorial pemula melibatkan fungsi yang paling biasa digunakan, tetapi terdapat banyak fungsi dan fungsi yang tidak akan dilindungi dalam tutorial ini. Anda perlu melihat dokumentasi API platform untuk mengetahui sendiri.Selepas anda selesai dengan tutorial ini, anda akan dapat menulis strategi yang lebih bebas dan lebih disesuaikan, dan platform FMZ hanyalah sebuah alat.
Platform FMZ dibungkus untuk semua bursa yang disokong, untuk mengekalkan keseragaman, sokongan API untuk bursa tunggal tidak lengkap. Sebagai contoh, mendapatkan K-line biasanya boleh dihantar ke jumlah K-line atau masa permulaan, sementara platform FMZ tetap, beberapa platform menyokong pesanan kuantiti, FMZ tidak disokong, dan sebagainya. Oleh itu, diperlukan cara untuk mengakses data bursa secara langsung.Untuk antara muka terbuka (jika ada), boleh digunakanHttpQueryUntuk menambah akaun, anda perlu menggunakan:IO。Untuk parameter input yang spesifik, sila rujuk kepada dokumentasi API bursa yang sesuai.InfoMedan ini mengembalikan maklumat asal, tetapi masih tidak dapat menyelesaikan masalah yang tidak menyokong antara muka.
Mengembalikan kandungan asal yang dikembalikan oleh permintaan REST API terakhir (string), yang boleh digunakan untuk menguraikan maklumat lanjutan 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 kepada antara muka terbuka, Js boleh digunakanHttpQueryPython sendiri boleh menggunakan pakej yang berkaitan, sepertiurllibataurequests。
HttpQuery adalah kaedah GET secara lalai, dan menyokong lebih banyak ciri, 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 permintaan 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 antara muka yang memerlukan tandatangan API-KEY, fungsi IO boleh digunakan, pengguna hanya perlu mengambil berat tentang parameter yang dihantar, proses tandatangan tertentu akan dilakukan oleh lapisan bawah.
Platform FMZ pada masa ini tidak menyokong perintah hentian BitMEX, melalui IO melalui langkah-langkah berikut:
https://www.bitmex.com/api/explorer/。https://www.bitmex.com/api/v1/order, kaedah untukPOSTOleh kerana FMZ telah menetapkan alamat root secara dalaman, anda hanya perlu memasukkan “/api/v1/order”.symbol=XBTUSD&side=Buy&orderQty=1&stopPx=4000&ordType=StopKode khusus:
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 wang digital menyokong keadaan penghantaran websocket, beberapa bursa menyokong maklumat kemas kini akaun websocket. Berbanding dengan Rest API, websocket umumnya mempunyai kelewatan yang rendah, frekuensi tinggi, tidak terhad kepada frekuensi Rest API platform dan sebagainya. Kelemahannya adalah masalah gangguan, pengendalian tidak intuitif.
Artikel ini akan memberi tumpuan kepada platform kuantitatif pencipta FMZ, menggunakan bahasa JavaScript, menggunakan fungsi dial yang terbungkus dalam platform untuk menyambung, spesifikasi dan parameter dalam dokumentasi, mencari dial, fungsi dial telah diperbaharui beberapa kali untuk melaksanakan pelbagai fungsi, artikel ini akan merangkumi ini, dan memperkenalkan strategi yang didorong oleh peristiwa berdasarkan wss, dan masalah menyambung beberapa bursa. Python juga boleh menggunakan fungsi dial, atau boleh menggunakan perpustakaan yang sesuai.
Secara amnya, ia boleh disambungkan secara langsung, seperti menghantar ticker keselamatan mata wang:
var client = Dial("wss://stream.binance.com:9443/ws/!ticker@arr")
Untuk data yang dikembalikan adalah format mampatan, anda perlu menentukan format mampatan pada sambungan, compress menentukan format mampatan, mod mewakili menghantar data yang dikembalikan yang memerlukan mampatan, seperti sambungan OKEx:
var client = Dial("wss://real.okex.com:10441/websocket?compress=true|compress=gzip_raw&mode=recv")
Fungsi dial menyokong penyambungan semula, dilakukan oleh bahasa Go yang mendasar, penyambungan semula penyambungan penyambungan yang terdeteksi, sangat mudah dan disyorkan untuk kandungan data permintaan yang sudah ada di url, seperti contoh Binance baru-baru ini. Untuk yang perlu menghantar pesanan pesanan, anda boleh mengekalkan mekanisme penyambungan semula.
var client = Dial("wss://stream.binance.com:9443/ws/!ticker@arr|reconnect=true")
Untuk mendapatkan maklumat lanjut, anda perlu mendaftar ke saluran yang anda ingin langgan, seperti Coinbase:
client = Dial("wss://ws-feed.pro.coinbase.com", 60)
client.write('{"type": "subscribe","product_ids": ["BTC-USD"],"channels": ["ticker","heartbeat"]}')
Websocket biasanya digunakan untuk membaca keadaan, tetapi juga boleh digunakan untuk mendapatkan pesanan, push akaun, push data terenkripsi ini kadang-kadang mempunyai kelewatan yang besar, perlu berhati-hati. Oleh kerana kaedah penyulitan lebih rumit, beberapa contoh diberikan di sini. Perhatikan bahawa hanya memerlukan AccessKey, boleh ditetapkan sebagai parameter dasar, jika memerlukan SecretKey, boleh digunakan untuk memanggil fungsi implisit exchange.HMAC) untuk memastikan keselamatan.
//火币期货推送例子
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)
}
}
Kod yang boleh dibaca secara berterusan dalam kitaran mati ialah:
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 adalah cepat, Go yang rendah akan menyimpan semua data dalam barisan, dan program yang dipanggil read, dan kemudian kembali. Pada cakera keras, perintah dan sebagainya akan membawa kelewatan, yang boleh menyebabkan pengumpulan data. Untuk penarikan transaksi, penarikan akaun, penarikan nilai mendalam dan sebagainya, kita memerlukan data sejarah, untuk data keadaan, kita kebanyakannya hanya peduli dengan yang terkini, tidak peduli dengan data sejarah.
read()Jika tidak ditambah parameter, data tertua akan dikembalikan, dan data yang tidak ada akan diblokir untuk dikembalikan. Jika anda mahu data terkini, anda boleh menggunakanclient.read(-2)Kembali ke data terkini, tetapi apabila tiada lagi data, kembali ke null, perlu dipertimbangkan untuk rujukan semula.
Read mempunyai parameter yang berbeza bergantung pada bagaimana ia menangani data lama yang disimpan dalam cache, dan sama ada ia tersumbat apabila tiada data, seperti gambar di bawah, yang kelihatan rumit, tetapi menjadikan program lebih fleksibel.

Dalam kes ini, prosedur jelas tidak dapat digunakan dengan read () mudah, kerana satu bursa akan menyekat mesej yang sedang menunggu, sementara bursa lain tidak akan menerima mesej baru walaupun ada. Kaedah rawatan 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
}
}
Bahagian ini agak rumit, kerana data push mungkin terputus, atau penundaan push sangat tinggi, walaupun dapat menerima heartbeat tidak bermakna data masih diarahkan, anda boleh menetapkan selang peristiwa, jika melebihi selang tidak menerima kemas kini, sambung semula, dan sebaiknya perbandingan dengan hasil yang dikembalikan oleh rest untuk melihat apakah data itu tepat. Untuk keadaan khusus seperti ini, anda boleh menetapkan sambungan semula secara automatik secara langsung.
Oleh kerana data push telah digunakan, program secara semula jadi juga harus ditulis sebagai pemandu peristiwa, perhatikan bahawa data push sering, tanpa terlalu banyak permintaan yang menyebabkan penutupan, biasanya 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 sambungan websocket, cara menghantar data, kandungan yang boleh melanggan, dan format data sering berbeza di setiap bursa, jadi platform tidak dibungkus dan perlu disambungkan sendiri dengan fungsi dial. Artikel ini meliputi beberapa perkara asas yang perlu diperhatikan, dan jika ada soalan, sila tanyakan.
PS. Sesetengah bursa walaupun tidak menyediakan websocket, tetapi sebenarnya menggunakan fungsi modem untuk masuk ke laman web, anda akan dapati bahawa mereka menggunakan push websocket, dan jika anda mengkaji, anda akan mendapati format langganan dan format pulangan.
JavaScript boleh dilaksanakan secara serentak melalui fungsi Go, dan Python boleh menggunakan perpustakaan multithread yang sesuai.
Dalam melaksanakan strategi kuantitatif, dalam banyak kes, pelaksanaan serentak dapat mengurangkan kecekapan penambahbaikan penundaan. Sebagai contoh, dalam strategi perlindungan, anda perlu mendapatkan kedalaman dua syiling, kod yang dijalankan mengikut urutan adalah seperti berikut:
var depthA = exchanges[0].GetDepth()
var depthB = exchanges[1].GetDepth()
Terdapat kelewatan dalam meminta API rehat. Anggapkan ia adalah 100ms Maka masa untuk mendapatkan kedalaman dua kali sebenarnya berbeza Jika lebih banyak akses diperlukan, masalah kelewatan akan menjadi lebih ketara, menjejaskan pelaksanaan strategi.
Oleh kerana JavaScript tidak mempunyai banyak benang, fungsi Go yang terbungkus di bawahnya menyelesaikan masalah ini. Fungsi Go boleh digunakan untuk API yang memerlukan akses rangkaian, sepertiGetDepth,GetAccountDan sebagainya.IOPanggilan seperti:exchange.Go("IO", "api", "POST", "/api/v1/contract_batchorder", "orders_data=" + JSON.stringify(orders))Namun, kerana mekanisme reka bentuk, ia agak sukar untuk dilaksanakan.
var a = exchanges[0].Go("GetDepth")
var b = exchanges[1].Go("GetDepth")
var depthA = a.wait() //调用wait方法等待返回异步获取depth结果
var depthB = b.wait()
Dalam kebanyakan kes mudah, tidak ada salahnya menulis dasar dengan cara ini. Tetapi ambil perhatian bahawa proses ini mesti diulang setiap kali gelung strategi, dan pembolehubah perantaraan a dan b sebenarnya hanya bantuan sementara. Jika kita mempunyai banyak tugas serentak, kita perlu merekodkan surat-menyurat antara a dan depthA, dan b dan depthB Apabila tugas serentak kita tidak pasti, keadaan menjadi lebih rumit. Oleh itu, kami ingin melaksanakan fungsi: apabila menulis Go concurrency, mengikat pembolehubah pada masa yang sama, dan apabila hasil larian serentak dikembalikan, hasilnya secara automatik diberikan kepada pembolehubah, dengan itu menghapuskan pembolehubah perantaraan dan menjadikan program lebih ringkas. Pelaksanaan khusus adalah seperti berikut:
function G(t, ctx, f) {
return {run:function(){
f(t.wait(1000), ctx)
}}
}
Kami mentakrifkan fungsi G, di mana parameter t ialah fungsi Go yang akan dilaksanakan, ctx ialah konteks program, dan f ialah fungsi untuk tugasan tertentu. Kita akan melihat fungsi ini beraksi sebentar lagi.
Pada ketika ini, keseluruhan rangka kerja program boleh ditulis sebagai model “pengeluar-pengguna” (dengan beberapa perbezaan Pengeluar secara berterusan mengeluarkan tugasan, dan pengguna melaksanakannya secara serentak Kod berikut hanya untuk demonstrasi dan tidak melibatkan program.
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)
}
}
Nampaknya kami hanya melaksanakan fungsi mudah selepas melalui banyak langkah, tetapi sebenarnya, kerumitan kod telah dipermudahkan. Kami hanya perlu mengambil berat tentang tugas yang perlu dihasilkan oleh program, dan pekerja (. ) program akan secara automatik melaksanakannya secara serentak dan mengembalikan hasil yang sepadan. Fleksibiliti telah banyak dipertingkatkan.
Tutorial Pemula Peta adalah perpustakaan grafik yang disyorkan, dan dalam kebanyakan kes dapat memenuhi keperluan. Jika anda memerlukan penyesuaian lebih lanjut, anda boleh menggunakan objek carta secara langsung.
Chart({…})Parameter dalaman adalah objek HighStock dan HighCharts, hanya menambah satu parameter tambahan__isStockUntuk membezakan sama ada HighStock. HighStock lebih memberi perhatian kepada carta mengikut urutan masa, oleh itu lebih biasa digunakan. FMZ menyokong modul asas HighCharts dan HighStock, tetapi tidak menyokong modul tambahan.
Contoh HighCharts tertentu: https://www.highcharts.com/demo ;contoh HighStock: https://www.highcharts.com/stock/demo 。 merujuk kod contoh ini, untuk mudah dipindahkan ke FMZ。
Anda boleh memanggil add ().[series index ((seperti 0), data]) menambah data ke series yang ditentukan indeks, memanggil reset ((() kosongkan data carta, reset boleh membawa parameter digital, tentukan jumlah baris yang disimpan. Ia menyokong memaparkan pelbagai carta, dan konfigurasi hanya perlu memasukkan parameter array seperti: var chart = Chart ((([{…}, {…}, {…}]), contohnya graf A mempunyai dua siri, graf B mempunyai satu siri, dan graf C mempunyai satu siri, maka ketika add menetapkan 0 dan 1 siri ID mewakili data dari dua siri dalam grafik 1 yang dikemas kini, ketika add menetapkan siri ID 2 merujuk kepada data dari siri pertama dalam grafik 2, dan menetapkan siri 3 merujuk kepada data dari siri pertama dalam grafik 3.
Contohnya:
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);
}
}
Satu contoh menggunakan susun atur carta: https://www.fmz.com/strategy/136056
Alamat sumber terbuka: https://github.com/fmzquant/backtest_python
Pemasangan
Ketik perintah berikut di baris perintah:
pip install https://github.com/fmzquant/backtest_python/archive/master.zip
Contoh mudah
Parameter pengembalian ditetapkan sebagai komen di awal kod dasar, lihat seting pengembalian yang disimpan di antara muka pengeditan dasar di laman 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
Ujian belakang
Oleh kerana strategi yang lengkap memerlukan kitaran mati, kecacatan EOF akan dibuang untuk menamatkan prosedur setelah pengesanan selesai, oleh itu toleransi diperlukan.
# !/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), menukar sumber data pengulangan, menggunakan data baris K yang disesuaikan. Parameter arr , adalah satu elemen untuk data baris K sebagai data tiang ((iaitu: Array data baris K, sementara hanya menyokong pengulangan JavaScript。
Format data bagi setiap elemen dalam susunan arr:
[
1530460800, // time 时间戳
2841.5795, // open 开盘价
2845.6801, // high 最高价
2756.815, // low 最低价
2775.557, // close 收盘价
137035034 // volume 成交量
]
Sumber data boleh diimport ke dalam perbendaharaan templat.
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: Apabila anda melakukan inisialisasi, anda mesti terlebih dahulu mengimport data tersuai (iaitu memanggil data tetapan fungsi exchange.SetData), dan kitaran data baris K tersuai mestilah selaras dengan kitaran baris K asas yang ditetapkan pada halaman pengukuran semula, iaitu: data baris K tersuai, satu baris K adalah 1 minit, maka kitaran baris K asas yang ditetapkan dalam pengukuran semula juga akan ditetapkan sebagai 1 minit.
Jika bursa yang tidak disokong dan bursa yang disokong API sama dengan yang sama, hanya alamat pangkalan yang berbeza, boleh disokong dengan menukar alamat pangkalan. Secara khusus untuk menambah bursa pilih bursa yang disokong, tetapi API-KEY mengisi bursa yang tidak disokong, gunakan IO dalam strategi untuk menukar alamat pangkalan, seperti:
exchange.IO("base", "http://api.huobi.pro")
//http://api.huobi.pro为为支持交易所API基地址,注意不用添加/api/v3之类的,会自动补全
Tidak semua bursa menyokong FMZ, tetapi platform ini menyediakan akses kepada protokol umum. Prinsipnya ialah:
Secara ringkasnya, protokol umum adalah sama dengan perantara, yang mewakili permintaan hos dan mengembalikan data mengikut piawaian yang sesuai. Kod protokol umum perlu diselesaikan sendiri, menulis protokol umum sebenarnya mewakili anda boleh mengakses bursa secara berasingan, menyelesaikan strategi. FMZ rasmi kadang-kadang menerbitkan versi exe protokol umum untuk bursa. Protokol umum juga boleh dilakukan dengan Python, yang boleh berfungsi sebagai cakera biasa di atas tuan rumah.
Perbincangan mengenai perjanjian: https://www.fmz.com/bbs-topic/1052 Contoh Python untuk menulis protokol umum: https://www.fmz.com/strategy/101399
Seperti pelbagai operasi yang boleh dilaksanakan melalui API, laman web FMZ juga berasaskan API. Anda boleh memohon API-KEY laman web FMZ anda sendiri untuk melaksanakan pelbagai fungsi seperti membuat, menghidupkan semula, menghapus cakera, mendapatkan senarai cakera, dan mendapatkan jurnal cakera.
Oleh kerana platform FMZ yang sangat berskala, anda boleh membuat platform kuantitatif anda sendiri berdasarkan API yang diperluaskan, dan membolehkan pengguna menjalankan cakera keras di platform anda.
Pasaran dagangan mata wang digital semakin menjadi perhatian pedagang kuantitatif kerana keistimewaannya, sebenarnya perdagangan berprogram telah menjadi arus utama mata wang digital, strategi seperti pasaran lindung nilai sentiasa aktif di pasaran. Bagi pemula yang ingin memasuki bidang ini, menghadapi banyak pertukaran dan API yang berubah-ubah, kesukaran berat sebelah.
PengiklananKursus perdagangan kuantitatif mata wang digital dalam kelas NetEase Cloud❚ Log masuk ke kelas NetEase Cloud, kongsi pautan kursus anda (pautan dengan courseId yang unik), orang lain mendaftar dan membeli kursus melalui pautan ini, anda akan mendapat 50% daripada jumlah 10 yuan. ❚ Perhatikan promosi kelas barang-barang kelas NetEase Cloud, anda boleh mendapatkan nombor awam WeChat.
Pengguna mengklik pautan promosi, dan mendaftar dalam tempoh enam bulan, kami akan mengembalikan komisen mengikut jumlah yang sah dalam pesanan yang sah. Komisen akan dikembalikan ke akaun pengiklan dalam bentuk poin, pengguna boleh menukar baki akaun platform perdagangan kuantitatif pencipta dengan nisbah 10:1, dan juga dapat menggunakan poin untuk menukar pencipta untuk mengukur barang-barang di sekitarnya.
Laman web FMZ lengkap dapat dikerahkan ke pelayan eksklusif perusahaan atau pasukan, untuk mencapai kawalan dan penyesuaian fungsi sepenuhnya. Laman web FMZ telah digunakan dan diuji oleh kira-kira 100,000 pengguna, mencapai ketersediaan dan keselamatan yang tinggi, dapat menjimatkan kos masa pasukan kuantitatif dan perusahaan.
Sistem profesional yang menyediakan kecairan pasaran dan pengurusan wang untuk bursa mungkin merupakan sistem perdagangan yang paling sempurna di pasaran dan digunakan oleh banyak bursa dan pasukan.
Sistem perdagangan teknologi pencipta menggunakan teknologi pencitraan memori, pemprosesan pesanan berkelajuan sehingga 2 juta pin / detik, dapat menjamin pemprosesan pesanan tidak akan mengalami sebarang kelewatan dan carton. Dapat mengekalkan operasi yang lancar dan stabil di bursa yang mempunyai lebih daripada 20 juta pengguna dalam talian pada masa yang sama.