[TOC] Sebelum anda belajar tutorial ini, anda perlu belajar terlebih dahulu.FMZ inventor menggunakan platform kuantiti untuk memulakandanFMZ Mengukur Platform Strategi Menulis Tutorial Rendah, dan mahir menggunakan bahasa pengaturcaraan.Tutorial awal melibatkan fungsi yang paling biasa digunakan, tetapi terdapat banyak lagi fungsi dan fungsi yang tidak diperkenalkan dan tidak akan dilindungi dalam tutorial ini, perlu melihat dokumentasi API platform untuk mengetahui sendiri.Setelah belajar tutorial ini, anda akan dapat menulis strategi yang lebih bebas dan disesuaikan, dan platform FMZ hanyalah satu alat.
Platform FMZ telah dibungkus untuk semua bursa yang disokong, untuk mengekalkan keseragaman, sokongan API untuk bursa individu tidak lengkap. Sebagai contoh, mendapatkan K-line biasanya boleh menghantar jumlah atau waktu permulaan K-line, sementara platform FMZ tetap, beberapa platform menyokong pesanan jumlah, FMZ tidak menyokong, dan sebagainya. Oleh itu, memerlukan kaedah untuk mengakses data bursa secara langsung.Untuk antara muka awam (seperti industri), boleh digunakanHttpQuery
, untuk menambah maklumat akaun, anda perlu menggunakanIO
。Untuk parameter penghantaran tertentu, lihat juga dokumen API pertukaran yang berkaitan.Info
Bidang itu mengembalikan maklumat asal, tetapi masih tidak dapat menyelesaikan masalah yang tidak menyokong antara muka.
Mengembalikan kandungan asal (string) yang dikembalikan pada permintaan REST API terakhir, yang boleh digunakan untuk menganalisis maklumat penyelenggaraan 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)
}
Untuk mengakses antara muka awam, Js boleh digunakanHttpQuery
Python boleh menggunakan pakej yang berkaitan sendiri, seperti:urllib
ataurequests
。
HttpQuery adalah kaedah GET secara lalai dan menyokong lebih banyak fungsi, 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 Python menggunakan permintaan
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 penghantaran, dan proses tandatangan tertentu akan dilakukan oleh lapisan bawah.
Platform FMZ tidak menyokong BitMEX Stop Loss Order pada masa ini.
https://www.bitmex.com/api/explorer/
。https://www.bitmex.com/api/v1/order
Cara ini ialahPOST
. Oleh kerana FMZ telah menetapkan alamat akar secara dalaman, anda hanya perlu memasukkan "/api/v1/order".symbol=XBTUSD&side=Buy&orderQty=1&stopPx=4000&ordType=Stop
Kode 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"}))
Contoh lain:https://www.fmz.com/bbs-topic/3683
Pada asasnya semua pertukaran mata wang digital menyokong pasaran penghantaran websocket, dan beberapa pertukaran menyokong websocket untuk mengemas kini maklumat akaun. Berbanding dengan rest API, websocket umumnya mempunyai kelewatan yang rendah, kekerapan yang tinggi, tidak terhad pada kekerapan platform rest API.
Artikel ini akan membincangkan mengenai platform kuantiti pada FMZ Inventor, menggunakan bahasa JavaScript, menggunakan fungsi Dial yang terbungkus platform untuk menyambung, penjelasan dan parameter dalam dokumen, mencari Dial, untuk mencapai pelbagai fungsi, fungsi Dial telah beberapa kali dikemas kini, dan ini akan meliputi ini, dan memperkenalkan strategi pemanduan peristiwa berasaskan wss, dan masalah menyambung pelbagai bursa. Python juga boleh menggunakan fungsi Dial, dan perpustakaan yang sesuai juga boleh digunakan.
Secara amnya, anda boleh menyambung secara langsung, seperti untuk mendapatkan ticker keselamatan yang didorong:
var client = Dial("wss://stream.binance.com:9443/ws/!ticker@arr")
Untuk data yang dikembalikan adalah format mampatan, yang diperlukan pada sambungan yang ditentukan, compress menetapkan format mampatan, mode mewakili menghantar data yang dikembalikan yang perlu dipadatkan, seperti sambungan OKEX:
var client = Dial("wss://real.okex.com:10441/websocket?compress=true|compress=gzip_raw&mode=recv")
Fungsi dial menyokong sambungan semula, yang dilakukan oleh bahasa Go yang mendasari, sambungan yang dikesan terputus akan disambung semula, sangat mudah digunakan untuk kandungan data permintaan yang sudah berada dalam url, seperti contoh yang baru-baru ini. Untuk orang yang perlu menghantar pesanan, anda boleh mengekalkan mekanisme sambungan semula sendiri.
var client = Dial("wss://stream.binance.com:9443/ws/!ticker@arr|reconnect=true")
Mesej langganan wss, permintaan dari beberapa bursa dalam url, dan beberapa saluran yang memerlukan langganan sendiri, 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 transaksi, tetapi juga boleh digunakan untuk mendapatkan pesanan, penyampaian akaun, dan penyampaian data enkripsi ini kadang-kadang akan berpanjangan, perlu digunakan dengan berhati-hati. Oleh kerana kaedah penyulitan lebih rumit, berikut adalah beberapa contoh rujukan. Perhatikan bahawa hanya memerlukan AccessKey, yang boleh ditetapkan sebagai parameter dasar, seperti memerlukan SecretKey, yang boleh digunakan untuk pertukaran.
//火币期货推送例子
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)
}
}
Pada umumnya, dalam kitaran mati, anda boleh terus membaca, kodnya seperti 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 yang dipindahkan dengan cepat, lapisan bawah Go akan menyimpan semua data dalam antrian, sementara program memanggil read, dan kemudian kembali; sementara operasi pada cakera sebenar akan membawa kelewatan, yang boleh menyebabkan pengumpulan data. Untuk pemindahan transaksi, pemindahan akaun, pemindahan nilai mendalam, dan lain-lain maklumat, kita memerlukan data sejarah, untuk data pasaran, kebanyakan kes kita hanya berminat dengan data terkini, tidak peduli dengan data sejarah.
read()
Jika tiada parameter, ia akan mengembalikan data tertua, dan jika tiada data, ia akan disekat untuk kembali.client.read(-2)
Mengembalikan data terkini dengan serta-merta, tetapi kembali kepada null apabila tidak ada lagi data, perlu membuat keputusan untuk merujuk semula.
Berdasarkan cara merawat data lama yang disimpan, dan sama ada ia akan tersumbat apabila tidak ada data, read mempunyai parameter yang berbeza, seperti yang ditunjukkan di bawah, yang kelihatan rumit tetapi menjadikan program lebih fleksibel.
Dalam kes ini, prosedur jelas tidak boleh menggunakan read sederhana (), kerana satu pertukaran akan menyumbat mesej yang menunggu, di mana pertukaran lain tidak akan menerima walaupun ada mesej baru.
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 terganggu, atau penundaan push sangat tinggi, walaupun menerima denyutan jantung tidak menunjukkan data masih diteruskan, anda boleh menetapkan selang peristiwa, bersambung semula jika tidak menerima kemas kini melebihi selang, dan lebih baik untuk membandingkan hasil yang dikembalikan dengan masa dan rest untuk melihat sama ada data tersebut adalah tepat. Untuk kes khas seperti Bitcoin, anda boleh menetapkan sambung semula automatik secara langsung.
Oleh kerana telah menggunakan data push, program secara semula jadi juga ditulis sebagai pemacu peristiwa, berhati-hati untuk mendorong data dengan kerap, tidak menggunakan banyak permintaan yang menyebabkan penutupan, secara amnya boleh 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 setiap bursa, cara menghantar data, kandungan yang boleh dilabel, format data sering berbeza, jadi platform tidak dibungkus, dan memerlukan sambungan sendiri dengan fungsi Dial. Artikel ini pada dasarnya merangkumi beberapa pertimbangan asas, jika ada soalan, selamat datang untuk bertanya.
PS. Sesetengah bursa walaupun tidak menawarkan pasaran websocket, tetapi sebenarnya menggunakan fungsi penyampaian laman web yang menggunakan fungsi penyampaian websocket, anda akan mendapati bahawa mereka menggunakan format langganan dan format pulangan. Sesetengahnya kelihatan seperti dienkripsi, yang dapat dilihat dengan base64 mengunci semula.
JavaScript boleh dilaksanakan secara serentak melalui fungsi Go, Python boleh menggunakan perpustakaan pelbagai thread yang sesuai.
Dalam banyak kes, pelaksanaan serentak boleh mengurangkan kelewatan untuk meningkatkan kecekapan dalam melaksanakan strategi kuantitatif. Sebagai contoh, dalam permainan sebenar strategi lindung nilai, anda perlu mendapatkan kedalaman dua koin, kod yang dilaksanakan mengikut urutan adalah sebagai berikut:
var depthA = exchanges[0].GetDepth()
var depthB = exchanges[1].GetDepth()
Permintaan sekali rest API mempunyai kelewatan, andaikan 100ms, maka masa untuk mendapatkan kedalaman dua kali sebenarnya tidak sama, jika memerlukan lebih banyak lawatan, masalah kelewatan akan menjadi lebih menonjol dan mempengaruhi pelaksanaan dasar.
Oleh kerana JavaScript tidak mempunyai banyak utas, fungsi Go di dalam lapisan bawah menyelesaikan masalah ini, fungsi Go boleh digunakan untuk API yang memerlukan akses rangkaian, seperti:GetDepth
,GetAccount
Dan seterusnya.IO
Perkataan seperti:exchange.Go("IO", "api", "POST", "/api/v1/contract_batchorder", "orders_data=" + JSON.stringify(orders))
Tetapi kerana mekanisme reka bentuk, pelaksanaan lebih sukar.
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 yang mudah, menulis dasar seperti ini tidak masalah. Tetapi perhatikan bahawa setiap putaran dasar mengulangi proses ini, dan pembolehubah tengah a, b sebenarnya hanya bantuan sementara. Jika kita mempunyai banyak tugas serentak, kita juga perlu merakam hubungan antara a dan kedalaman A, b dan kedalaman B, dan keadaan lebih rumit apabila tugas serentak kita tidak pasti. Oleh itu, kita ingin melaksanakan fungsi: apabila menulis Go serentak, mengikat satu pembolehubah pada masa yang sama, dan apabila hasil pelaksanaan serentak dikembalikan, hasilnya akan memberi nilai kepada pembolehubah secara automatik, dengan itu menghilangkan pembolehubah tengah, untuk menjadikan program lebih ringkas. Secara khusus, dilaksanakan seperti:
function G(t, ctx, f) {
return {run:function(){
f(t.wait(1000), ctx)
}}
}
Kami mentakrifkan fungsi G, di mana parameter t adalah fungsi Go yang akan dijalankan, ctx adalah konteks dalam program yang dicatatkan, dan f adalah fungsi yang diberikan nilai tertentu.
Dalam kes ini, kerangka kerja program secara keseluruhan boleh ditulis sebagai serupa dengan model kerangka pengeluar-pengguna kerangka kerja (dengan beberapa perbezaan), di mana pengeluar terus mengeluarkan tugas, pengguna melaksanakan mereka secara serentak, dan kod hanya untuk demonstrasi, tidak melibatkan logika pelaksanaan 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)
}
}
Ia kelihatan seperti pusingan yang hanya mencapai satu fungsi yang mudah, sebenarnya sangat memudahkan kerumitan kod, kita hanya perlu mengambil berat tentang apa tugas yang perlu dihasilkan oleh program, yang secara automatik dijalankan secara serentak oleh program worker dan mengembalikan hasil yang sesuai. Kelembapan meningkat banyak.
Tutorial awal memperkenalkan grafik adalah perpustakaan grafik yang disyorkan, yang boleh memenuhi kebanyakan keperluan. Jika keperluan penyesuaian lebih lanjut diperlukan, objek Chart boleh dioperasikan secara langsung.
Chart({…})
Parameter dalaman adalah objek HighStock dan HighCharts, hanya menambahkan satu parameter tambahan__isStock
Untuk membezakan sama ada ia adalah HighStock. HighStock lebih memberi tumpuan kepada grafik urutan masa dan oleh itu lebih biasa digunakan. FMZ menyokong modul asas HighCharts dan HighStock, tetapi tidak menyokong modul tambahan.
Contohnya HighCharts:https://www.highcharts.com/demoContoh HighStock:https://www.highcharts.com/stock/demo◦ Menggunakan contoh-contoh ini, kod boleh dipindahkan ke FMZ.
Anda boleh memanggil add (([series indeks ((seperti 0), data]) untuk menambah data ke dalam siri indeks yang ditentukan, memanggil reset (seperti 0), data grafik kosong, reset boleh membawa parameter nombor, menentukan baris yang dikekalkan. Mendukung untuk memaparkan pelbagai carta, anda hanya perlu memasukkan parameter array apabila disesuaikan, seperti: var chart = Chart (([{...}, {...}, {...})), contohnya carta satu mempunyai dua siri, carta mempunyai satu siri, carta tiga siri, maka apabila menambahkan satu siri dengan 01, ID seri mewakili data dua siri baru dalam carta 1, apabila menambahkan satu siri dengan ID seri mewakili data dua siri baru dalam carta 1, apabila menetapkan siri 2 menunjukkan data siri pertama dalam carta 2, menetapkan siri 3 menunjukkan data siri pertama dalam carta 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);
}
}
Contoh yang menggunakan susun atur grafik:https://www.fmz.com/strategy/136056
Alamat sumber terbuka:https://github.com/fmzquant/backtest_python
Pemasangan
Masukkan arahan berikut di bar arahan:
pip install https://github.com/fmzquant/backtest_python/archive/master.zip
Contoh mudah
Parameter retargeting ditetapkan sebagai nota di awal kod dasar, lihat lebih lanjut pada antara muka penyunting dasar 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 semula
Oleh kerana strategi yang lengkap memerlukan pusingan mati, EOF yang luar biasa akan dibuang untuk menamatkan prosedur selepas ujian semula selesai, dan oleh itu perlu dilakukan dengan baik.
# !/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 ulangan, menggunakan data baris K yang disesuaikan. Array arr, adalah satu elemen yang merupakan susunan data baris K (iaitu: susunan data baris K, sementara hanya menyokong ulangan JavaScript).
Format data elemen tunggal dalam array arr ialah:
[
1530460800, // time 时间戳
2841.5795, // open 开盘价
2845.6801, // high 最高价
2756.815, // low 最低价
2775.557, // close 收盘价
137035034 // volume 成交量
]
Sumber data boleh diimport ke dalam perpustakaan jenis 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("导入数据成功")
}
Nota: Perlu diperhatikan bahawa data kustom (iaitu panggilan data tetapan fungsi exchange.SetData) harus diimport terlebih dahulu semasa permulaan, dan kitaran data baris K yang disesuaikan mestilah sepadan dengan kitaran K baris bawah yang ditetapkan pada tetapan halaman retargeting, iaitu: data baris K yang disesuaikan, masa satu baris K adalah 1 minit, maka kitaran baris K baris bawah yang ditetapkan dalam retargeting juga ditetapkan untuk 1 minit.
Jika pertukaran yang tidak disokong dan API pertukaran yang disokong sama sekali, hanya alamat pangkalan yang berbeza, boleh disokong dengan cara menukar alamat pangkalan. Khususnya untuk memilih pertukaran yang disokong ketika menambahkan pertukaran, tetapi API-KEY mengisi pertukaran yang tidak disokong, dalam dasar, gunakan IO untuk menukar alamat pangkalan, seperti:
exchange.IO("base", "http://api.huobi.pro")
//http://api.huobi.pro为为支持交易所API基地址,注意不用添加/api/v3之类的,会自动补全
Walaupun tidak semua bursa menyokong FMZ, platform ini menyediakan akses kepada protokol umum; prinsipnya ialah:
Secara ringkasnya, protokol umum adalah seperti seorang perantara yang bertindak balas atas permintaan hos dan mengembalikan data mengikut piawaian yang sesuai. Kod protokol umum perlu diselesaikan sendiri, menulis protokol umum sebenarnya mewakili bahawa anda boleh mengakses pertukaran secara berasingan dan menyelesaikan dasar. Pegawai FMZ kadang-kadang mengeluarkan versi exe protokol umum pertukaran. Protokol umum juga boleh digunakan untuk menyelesaikan Python, di mana ia boleh berjalan seperti cakera fizikal biasa pada hos.
Perkataan mengenai perjanjian ini:https://www.fmz.com/bbs-topic/1052Contoh Python menulis protokol umum:https://www.fmz.com/strategy/101399
Seperti mana pelbagai operasi boleh dilaksanakan melalui API, laman web FMZ juga berasaskan API, anda boleh memohon pelaksanaan API-KEY laman web FMZ anda sendiri, seperti membuat, memulakan semula, memadam cakera sebenar, mendapatkan senarai cakera sebenar, mendapatkan log cakera sebenar, dan pelbagai fungsi, lihat dokumen API untuk FMZ Platform Extension API.
Oleh kerana skalabiliti yang kuat pada platform FMZ, anda boleh membuat platform kuantitatif anda sendiri berdasarkan API pelanjutan, membolehkan pengguna menjalankan cakera sebenar di platform anda dan sebagainya.
Pasaran dagangan mata wang digital semakin mendapat perhatian daripada peniaga kuantitatif kerana keunikan mereka, sebenarnya dagangan berprogram sudah menjadi arus utama mata wang digital, strategi seperti melakukan pasaran lindung nilai tidak pernah aktif di pasaran. Dan pemula yang mempunyai asas pengaturcaraan yang lemah ingin memasuki bidang ini, menghadapi banyak pertukaran dan API yang pelbagai, kesukaran berat. Pencipta (FMZ) platform kuantitatif (asli BotVs,www.fmz.comPerisian ini adalah platform dan komuniti kuantiti mata wang digital terbesar yang telah membantu beribu-ribu pemula dalam perdagangan kuantiti selama lebih dari empat tahun.
PemasaranKursus transaksi kuantitatif mata wang digital di kelas awan mudah◎ Log masuk ke kelas awan mudah, berkongsi pautan kursus anda (pautan dengan kursus ID yang unik), orang lain mendaftar dan membeli kursus melalui pautan ini, anda akan mendapat bahagian 50% sebanyak 10 yuan.
Pengguna mengklik pautan promosi, dan dalam tempoh enam bulan mendaftar untuk mengisi semula, kami akan mengembalikan komisen mengikut jumlah yang sah dalam pesanan yang sah. Komisen akan dikembalikan dalam bentuk mata wang kepada akaun penggalak, pengguna boleh menukar baki akaun platform dagangan inventor kuantitatif pada nisbah 10: 1, atau boleh menukar barang sekitar inventor kuantitatif pada masa akan datang. Pautan aktiviti khusus:https://www.fmz.com/bbs-topic/3828
Laman web FMZ lengkap boleh digunakan pada pelayan eksklusif syarikat atau pasukan, untuk mencapai kawalan dan fungsi yang disesuaikan sepenuhnya. Laman web FMZ telah digunakan dan diuji oleh kira-kira 100,000 pengguna, mencapai ketersediaan dan keselamatan yang tinggi, menjimatkan kos masa pasukan kuantitatif dan syarikat. Versi perusahaan yang ditujukan kepada pasukan dagangan kuantitatif kecil dan menengah, pelayan niaga hadapan komoditi, dan lain-lain, pengurus tawaran khusus.
Sistem profesional yang menyediakan kecairan pasaran dan pengurusan dana untuk bursa mungkin merupakan sistem yang paling lengkap di pasaran dan digunakan oleh banyak bursa dan pasukan.
Sistem dagangan teknologi pencipta menggunakan teknologi pemotongan memori, pemprosesan pesanan dengan kelajuan sehingga 2 juta keping / saat, yang dapat memastikan pemprosesan pesanan tidak mengalami kelewatan dan karton. Ia dapat mengekalkan pertukaran yang lancar dan stabil dengan jumlah pengguna dalam talian lebih dari 20 juta pada masa yang sama. Arkitektur sistem pelbagai lapisan, pelbagai kumpulan memastikan keselamatan sistem, kestabilan, mudah berkembang.
bbbwwed2009Adakah anda masih boleh menyertai kumpulan?
MAIKEOTuhan Rumput, WWE!!!
Rumput`` // Contoh Token Futures Push var ACCESSKEYID = 'kunci akses akaun token anda' var apiClient = Dial (('wss://api.hbdm.com/notification incompress=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.toISOSstring (().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}}") ``
Brigjen BeatJika websocket disambungkan ke pasaran langganan, maka mendapatkan data pasaran menggunakan fungsi read ((), kan? Jika menggunakan exchange.GetTicker ((), bukan mengambil data dari cache tempatan, tetapi memulakan permintaan rest untuk mengembalikan data, kan? Hanya Token One yang menyokong perubahan penerimaan pasaran melalui exchange.IO (("websocket") dan kemudian menggunakan exchange.GetTicker (() dan exchange.GetDepth (()) tidak akan meminta data kepada bursa rest, dan mendapatkan data dalam pasaran langganan yang telah diterima yang terdapat dalam penyangga tempatan. Saya faham, kan?
RumputMengikuti petua pengesahan
jsyzliuyuSila rujuk maklumat pengesahan di bawah untuk kumpulan Telegram FMZ sekarang:
ShaltielBaiklah.
RumputYa, lebih baik semua websocket menggunakan Dial, lebih mudah dikendalikan