[TOC] Trước khi học bài học này, bạn cần phải họcNgười phát minh FMZ sử dụng nền tảng định lượngvàFMZ định lượng nền tảng chiến lược viết hướng dẫn đầu tiênCác bạn có thể tham gia vào chương trình này bằng cách sử dụng các ngôn ngữ khác nhau.Các hướng dẫn ban đầu liên quan đến các chức năng được sử dụng phổ biến nhất, nhưng có rất nhiều chức năng và chức năng không được giới thiệu và sẽ không được bao gồm trong hướng dẫn này.Sau khi hoàn thành hướng dẫn này, bạn sẽ có thể viết các chiến lược tự do và tùy chỉnh hơn, nền tảng FMZ chỉ là một công cụ.
Các nền tảng FMZ được đóng gói cho tất cả các sàn giao dịch được hỗ trợ, để duy trì sự thống nhất, hỗ trợ API cho các sàn giao dịch cá nhân không đầy đủ. Ví dụ: thu thập K-Line thường có thể truyền vào số lượng K-Line hoặc thời gian bắt đầu, trong khi nền tảng FMZ là cố định, một số nền tảng hỗ trợ đặt hàng số lượng lớn, FMZ không hỗ trợ, v.v.Có thể sử dụng cho các giao diện công khai (như thị trường)HttpQuery
, cần sử dụng để thêm thông tin về tài khoảnIO
。Các tham số truyền thông cụ thể, cũng nên tham khảo tài liệu API giao dịch tương ứng.Info
Các trường trả về thông tin ban đầu, nhưng vẫn không thể giải quyết vấn đề không hỗ trợ giao diện.
Trả về nội dung nguyên bản (string) từ lần yêu cầu REST API cuối cùng được trả về và có thể tự phân tích thông tin mở rộng.
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)
}
Truy cập vào giao diện công khai, Js có thể sử dụngHttpQuery
Python có thể tự sử dụng các gói liên quan như:urllib
Hoặcrequests
。
HttpQuery là phương pháp GET mặc định, và hỗ trợ nhiều tính năng hơn, xem tài liệu 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"))
Ví dụ về Python sử dụng requests
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()
Đối với các giao diện cần chữ ký API-KEY, bạn có thể sử dụng hàm IO, người dùng chỉ cần quan tâm đến các tham số nhập, và quá trình ký hiệu cụ thể sẽ được thực hiện bởi tầng dưới.
FMZ hiện không hỗ trợ BitMEX Stop Loss, theo các bước sau:
https://www.bitmex.com/api/explorer/
。https://www.bitmex.com/api/v1/order
Phương pháp là:POST
Vì FMZ đã chỉ định địa chỉ gốc bên trong, chỉ cần nhập "/api/v1/order" là xong.symbol=XBTUSD&side=Buy&orderQty=1&stopPx=4000&ordType=Stop
Mã cụ thể:
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"}))
Ví dụ khác về IO:https://www.fmz.com/bbs-topic/3683
Về cơ bản, tất cả các sàn giao dịch tiền kỹ thuật số đều hỗ trợ websocket để gửi thông tin, một số sàn giao dịch hỗ trợ websocket để cập nhật thông tin tài khoản.
Bài viết này sẽ chủ yếu giới thiệu về nền tảng định lượng của nhà phát minh FMZ, sử dụng ngôn ngữ JavaScript để kết nối, sử dụng các hàm Dial được đóng gói trên nền tảng, hướng dẫn cụ thể và tham số trong tài liệu, tìm kiếm Dial, để thực hiện các chức năng khác nhau, hàm Dial đã được cập nhật một vài lần, bài viết này sẽ bao gồm điều này và giới thiệu các chiến lược điều khiển sự kiện dựa trên wss, và các vấn đề liên kết nhiều sàn giao dịch. Python cũng có thể sử dụng các hàm Dial và có thể sử dụng thư viện tương ứng.
Thông thường, người dùng có thể kết nối trực tiếp, ví dụ:
var client = Dial("wss://stream.binance.com:9443/ws/!ticker@arr")
Đối với dữ liệu được trả về là định dạng nén, cần thiết trong kết nối được chỉ định, compress chỉ định định dạng nén, mode đại diện cho việc gửi dữ liệu được trả lại cần nén, ví dụ như kết nối OKEX:
var client = Dial("wss://real.okex.com:10441/websocket?compress=true|compress=gzip_raw&mode=recv")
Chức năng Dial hỗ trợ nối lại, được thực hiện bởi ngôn ngữ Go dưới cùng, kết nối bị ngắt và nối lại được phát hiện, rất thuận tiện, nên sử dụng cho nội dung dữ liệu yêu cầu đã có trong url, ví dụ như ví dụ của Binance. Đối với những người cần gửi tin nhắn, bạn có thể tự duy trì cơ chế nối lại.
var client = Dial("wss://stream.binance.com:9443/ws/!ticker@arr|reconnect=true")
Một số kênh khác cũng yêu cầu bạn đăng ký, chẳng hạn như Coinbase:
client = Dial("wss://ws-feed.pro.coinbase.com", 60)
client.write('{"type": "subscribe","product_ids": ["BTC-USD"],"channels": ["ticker","heartbeat"]}')
Thông thường, websocket được sử dụng để đọc các giao dịch, nhưng cũng có thể được sử dụng để lấy đơn đặt hàng, chuyển giao tài khoản, chuyển giao dữ liệu mã hóa như vậy đôi khi có thể bị trì hoãn rất nhiều, nên cần thận trọng. Vì phương pháp mã hóa phức tạp hơn, đây là một vài ví dụ để tham khảo.
//火币期货推送例子
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)
}
}
Thông thường, trong chu kỳ chết, các mã được đọc liên tục như sau:
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 dữ liệu đẩy rất nhanh, tầng dưới của Go sẽ trì hoãn tất cả các dữ liệu trong hàng đợi, cho đến khi chương trình gọi read, và sau đó trở lại. Trong khi đó, các hoạt động theo thứ tự dưới của ổ đĩa thực sẽ mang lại sự trì hoãn, có thể gây ra sự tích lũy dữ liệu. Đối với chuyển giao đẩy, chuyển giao tài khoản, chuyển giao sâu, chuyển giao sâu, v.v. thông tin như vậy, chúng ta cần dữ liệu lịch sử, đối với dữ liệu thị trường, hầu hết các trường hợp chúng ta chỉ quan tâm đến dữ liệu mới nhất, không quan tâm đến dữ liệu lịch sử.
read()
Nếu không thêm các tham số, nó sẽ trả về dữ liệu cũ nhất, và nếu không có dữ liệu, chặn để trả về. Nếu bạn muốn dữ liệu mới nhất, bạn có thể sử dụngclient.read(-2)
Trả về dữ liệu mới nhất ngay lập tức, nhưng trả về null khi không có dữ liệu nữa, cần phải phán đoán và tham khảo lại.
Theo cách xử lý dữ liệu cũ được lưu trữ và liệu có bị tắc khi không có dữ liệu, read có các tham số khác nhau, cụ thể như hình dưới, trông phức tạp nhưng giúp cho chương trình linh hoạt hơn.
Đối với trường hợp này, rõ ràng là không thể sử dụng đơn giản read (), bởi vì một sàn giao dịch sẽ tắc nghẽn thông báo chờ, trong đó sàn giao dịch khác sẽ không nhận được thông báo ngay cả khi có thông báo mới.
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
}
}
Phần xử lý này khá khó khăn vì dữ liệu đẩy có thể bị gián đoạn, hoặc thời gian đẩy quá cao, thậm chí nếu nhận được nhịp tim cũng không có nghĩa là dữ liệu vẫn đang được đẩy, bạn có thể thiết lập một khoảng thời gian sự kiện, nếu vượt quá khoảng thời gian không nhận được bản cập nhật, bạn sẽ kết nối lại, và tốt nhất bạn nên phân biệt một khoảng thời gian và kết quả rest trở lại để xem dữ liệu có chính xác hay không. Đối với trường hợp đặc biệt như Bitcoin, bạn có thể đặt tự động nối lại trực tiếp.
Vì đã sử dụng dữ liệu đẩy, các chương trình tự nhiên cũng phải được viết như một trình điều khiển sự kiện, hãy chú ý đẩy dữ liệu thường xuyên, không cần quá nhiều yêu cầu dẫn đến khóa, thường có thể được viết như sau:
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)
}
}
Cách kết nối của các socket web của các sàn giao dịch khác nhau, cách gửi dữ liệu, nội dung có thể đăng ký, định dạng dữ liệu thường khác nhau, vì vậy nền tảng không được đóng gói và cần tự kết nối bằng chức năng Dial.
PS. Một số sàn giao dịch mặc dù không cung cấp thị trường websocket, nhưng thực sự sử dụng chức năng phím để truy cập trang web, bạn sẽ thấy tất cả các websocket được sử dụng để đẩy, tìm hiểu và bạn sẽ thấy định dạng đăng ký và định dạng trả về. Một số trông giống như được mã hóa, có thể được giải mã bằng base64.
JavaScript có thể được thực hiện đồng bộ thông qua hàm Go, Python có thể sử dụng thư viện nhiều chuỗi tương ứng.
Trong thực hiện chiến lược định lượng, trong nhiều trường hợp, việc thực hiện song song có thể làm giảm hiệu quả tăng độ trì hoãn.
var depthA = exchanges[0].GetDepth()
var depthB = exchanges[1].GetDepth()
Một lần yêu cầu rest API có sự chậm trễ, giả sử là 100ms, thì hai lần lấy độ sâu thực sự không giống nhau, nếu cần nhiều truy cập hơn, vấn đề chậm trễ sẽ nổi bật hơn, ảnh hưởng đến việc thực hiện chính sách.
Vì JavaScript không có nhiều chuỗi, nên hàm Go được đóng gói ở tầng dưới để giải quyết vấn đề này. Các hàm Go có thể được sử dụng cho các API cần truy cập mạng như:GetDepth
,GetAccount
Và như vậy.IO
Những người khác cũng có thể sử dụng các từ như:exchange.Go("IO", "api", "POST", "/api/v1/contract_batchorder", "orders_data=" + JSON.stringify(orders))
Tuy nhiên, do cơ chế thiết kế, việc thực hiện khá khó khăn.
var a = exchanges[0].Go("GetDepth")
var b = exchanges[1].Go("GetDepth")
var depthA = a.wait() //调用wait方法等待返回异步获取depth结果
var depthB = b.wait()
Trong hầu hết các trường hợp đơn giản, viết chính sách như vậy là không có vấn đề. Nhưng lưu ý rằng mỗi vòng lặp chính sách lặp lại quá trình này, các biến trung gian a, b thực sự chỉ là trợ giúp tạm thời. Nếu chúng ta có rất nhiều nhiệm vụ đồng thời, chúng ta cần ghi lại mối quan hệ tương ứng giữa a và độ sâu A, b và độ sâu B, điều này trở nên phức tạp hơn khi nhiệm vụ đồng thời của chúng ta không chắc chắn. Do đó, chúng ta muốn thực hiện một hàm: khi viết Go đồng thời, ràng buộc một biến, khi trả về kết quả chạy đồng thời, kết quả tự động gán giá trị cho biến, do đó bỏ qua các biến trung gian, làm cho chương trình ngắn gọn hơn.
function G(t, ctx, f) {
return {run:function(){
f(t.wait(1000), ctx)
}}
}
Chúng ta định nghĩa một hàm G, trong đó tham số t là hàm Go sẽ được thực hiện, ctx là ngữ cảnh của chương trình ghi lại, f là hàm được gán cụ thể.
Trong trường hợp này, một khuôn khổ chương trình tổng thể có thể được viết giống như một mô hình sản xuất-đầu hàng (có một số khác biệt), trong đó nhà sản xuất liên tục phát hành các nhiệm vụ, người tiêu dùng thực hiện chúng cùng một lúc, và mã chỉ là một bản trình bày, không liên quan đến logic thực thi của chương trình.
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)
}
}
Có vẻ như một vòng quay chỉ thực hiện một chức năng đơn giản, thực sự đơn giản hóa rất nhiều mức độ phức tạp của mã, chúng ta chỉ cần quan tâm đến những nhiệm vụ mà chương trình cần tạo ra, và các chương trình worker () tự động thực hiện chúng song song và trả về kết quả tương ứng.
Các hướng dẫn ban đầu giới thiệu đồ họa là một thư viện đồ họa được đề xuất, hầu hết các trường hợp có thể đáp ứng nhu cầu. Nếu cần tùy chỉnh thêm, bạn có thể trực tiếp vận hành các đối tượng Chart.
Chart({…})
Các tham số bên trong là HighStock và HighCharts đối tượng, chỉ cần thêm một tham số__isStock
FMZ hỗ trợ các mô-đun cơ bản của HighCharts và HighStock nhưng không hỗ trợ các mô-đun bổ sung.
Ví dụ cụ thể về HighCharts:https://www.highcharts.com/demoVí dụ HighStock:https://www.highcharts.com/stock/demoCác ví dụ trên có thể được chuyển sang FMZ một cách dễ dàng.
Bạn có thể gọi add (([series index ((ví dụ như 0), dữ liệu]) để thêm dữ liệu vào series của chỉ mục được chỉ định, gọi reset (ví dụ như 0), dữ liệu biểu đồ trống, reset có thể có một tham số số, chỉ định mục được lưu giữ. Hỗ trợ hiển thị nhiều biểu đồ, chỉ cần nhập tham số bảng tính khi cấu hình, ví dụ: var chart = Chart (([{...}, {...}, {...}), ví dụ như biểu đồ một có hai series, biểu đồ có một series, biểu đồ ba series, sau đó add khi chỉ định một chuỗi 01 với ID series đại diện cho dữ liệu của hai chuỗi biểu đồ 1, add khi chỉ định chuỗi ID 2 cho dữ liệu đầu tiên của biểu đồ 2 và chỉ định chuỗi 3 cho dữ liệu đầu tiên của biểu đồ 3.
Một ví dụ cụ thể:
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);
}
}
Một ví dụ sử dụng bố cục biểu đồ:https://www.fmz.com/strategy/136056
Địa chỉ nguồn mở cụ thể:https://github.com/fmzquant/backtest_python
Thiết lập
Nhập lệnh sau đây vào dòng lệnh:
pip install https://github.com/fmzquant/backtest_python/archive/master.zip
Ví dụ đơn giản
Các tham số kiểm tra lại được đặt dưới dạng chú thích ở đầu mã chính sách, cụ thể xem giao diện chỉnh sửa chính sách của trang 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
Kiểm tra lại
Vì một chiến lược hoàn chỉnh đòi hỏi một vòng lặp chết, EOF bất thường sẽ được ném ra để chấm dứt quá trình sau khi kiểm tra lại, do đó cần phải chấp nhận sai lầm.
# !/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), chuyển đổi nguồn dữ liệu kiểm tra lại, sử dụng dữ liệu đường K tùy chỉnh. Các tham số arr, là một phần tử của một mảng dữ liệu cột đường K (tức là: mảng dữ liệu đường K, tạm thời chỉ hỗ trợ kiểm tra lại JavaScript).
Trong một mảng arr, định dạng dữ liệu của từng phần tử là:
[
1530460800, // time 时间戳
2841.5795, // open 开盘价
2845.6801, // high 最高价
2756.815, // low 最低价
2775.557, // close 收盘价
137035034 // volume 成交量
]
Nguồn dữ liệu có thể được nhập vào thư viện kiểu mẫu.
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("导入数据成功")
}
Lưu ý: phải nhập dữ liệu tùy chỉnh trước khi khởi động (tức là gọi dữ liệu thiết lập của hàm exchange.SetData), chu kỳ dữ liệu đường K tùy chỉnh phải phù hợp với chu kỳ đường K dưới cùng được thiết lập trên trang kiểm tra, tức là: dữ liệu đường K tùy chỉnh, một thời gian đường K là 1 phút, thì chu kỳ đường K dưới cùng được thiết lập trong kiểm tra cũng được thiết lập là 1 phút.
Nếu API của sàn giao dịch không hỗ trợ và sàn giao dịch đã hỗ trợ là hoàn toàn giống nhau, chỉ có địa chỉ cơ sở khác nhau, bạn có thể hỗ trợ bằng cách chuyển địa chỉ cơ sở.
exchange.IO("base", "http://api.huobi.pro")
//http://api.huobi.pro为为支持交易所API基地址,注意不用添加/api/v3之类的,会自动补全
Không phải tất cả các sàn giao dịch đều hỗ trợ FMZ, nhưng nền tảng này cung cấp cách truy cập vào giao thức chung.
Nói một cách đơn giản, giao thức chung tương đương với một người trung gian, đại diện cho yêu cầu của người quản lý và trả lại dữ liệu theo tiêu chuẩn tương ứng. Mã giao thức chung cần tự hoàn thành, viết giao thức chung thực sự đại diện cho việc bạn có thể truy cập riêng vào sàn giao dịch và hoàn thành chính sách. FMZ chính thức đôi khi phát hành phiên bản exe của giao thức chung.
Thông tin chi tiết về thỏa thuận:https://www.fmz.com/bbs-topic/1052Một ví dụ về việc viết một giao thức chung trong Python:https://www.fmz.com/strategy/101399
Các hoạt động khác nhau của sàn giao dịch có thể được thực hiện thông qua API, trang web FMZ cũng dựa trên API, bạn có thể áp dụng trang web FMZ API-KEY của riêng bạn để thực hiện các chức năng như tạo, khởi động lại, xóa ổ đĩa thực, truy cập danh sách ổ đĩa thực, truy cập nhật ký ổ đĩa thực, v.v.
Với khả năng mở rộng mạnh mẽ của nền tảng FMZ, bạn có thể tạo nền tảng định lượng của riêng bạn dựa trên API mở rộng, cho phép người dùng chạy đĩa thực trên nền tảng của bạn.
Thị trường giao dịch tiền kỹ thuật số đang ngày càng được quan tâm bởi các nhà giao dịch định lượng do đặc tính của nó, thực tế giao dịch lập trình đã là dòng chính của tiền kỹ thuật số, các chiến lược như làm mạo hiểm thị trường luôn hoạt động trên thị trường.www.fmz.com(Mỹ) là cộng đồng và nền tảng định lượng tiền kỹ thuật số lớn nhất hiện nay và đã giúp hàng ngàn người mới bắt đầu giao dịch định lượng trong hơn 4 năm.
Phổ biếnChương trình giao dịch định lượng tiền kỹ thuật số trong lớp học điện toán đám mâyBạn sẽ được chia sẻ 50% tổng cộng 10 đô la. Hãy chú ý đến các khóa học tinh tế của lớp học tinh tế, quảng bá các con số cộng đồng WeChat ngay lập tức. Chào mừng tất cả mọi người mời người khác, quảng bá trên nhóm QQ trên microblog.
Người tiêu dùng nhấp vào liên kết quảng cáo, và đăng ký nạp tiền trong vòng sáu tháng, chúng tôi sẽ hoàn trả hoa hồng theo số tiền có hiệu lực trong đơn đặt hàng hiệu lực. Phí hoa hồng sẽ được trả lại dưới dạng điểm số vào tài khoản của người quảng cáo, người dùng có thể đổi theo tỷ lệ 10: 1 cho các nhà phát minh định lượng số dư tài khoản nền tảng giao dịch, hoặc có thể đổi lại các nhà phát minh định lượng hàng hóa xung quanh theo điểm số sau đó.https://www.fmz.com/bbs-topic/3828
Có thể triển khai toàn bộ trang web FMZ lên máy chủ độc quyền của doanh nghiệp hoặc nhóm, để có thể kiểm soát và tùy chỉnh đầy đủ các chức năng. Trang web FMZ đã được sử dụng và kiểm tra bởi khoảng 100.000 người dùng, đạt được khả năng sử dụng và bảo mật cao, tiết kiệm chi phí thời gian và thời gian cho các nhóm định lượng và doanh nghiệp. Phiên bản doanh nghiệp dành cho các nhóm giao dịch định lượng vừa và trung bình, dịch vụ tương lai hàng hóa, và các nhà quản lý giao dịch cụ thể.
Một hệ thống chuyên nghiệp cung cấp cho sàn giao dịch tính thanh khoản và quản lý tiền tệ có thể là hệ thống giao dịch hoàn hảo nhất trên thị trường, được sử dụng bởi nhiều sàn giao dịch và nhóm.
Hệ thống giao dịch công nghệ của nhà phát minh sử dụng công nghệ chụp trong bộ nhớ, tốc độ xử lý đơn đặt hàng lên đến 2 triệu bit / giây, có thể đảm bảo việc xử lý đơn đặt hàng không xảy ra bất kỳ sự chậm trễ và carton. Có thể duy trì hoạt động ổn định và ổn định của các sàn giao dịch với số lượng người dùng trực tuyến trên 20 triệu đồng thời. Kiến trúc hệ thống đa lớp, đa tập hợp đảm bảo an toàn, ổn định, dễ mở rộng của hệ thống.
bbbwwed2009Bạn có thể tham gia nhóm này không?
MAIKEOThiên thần cỏ, võ thuật!!!
Cỏ nhỏ`` // ví dụ về token futures push var ACCESSKEYID = 'chìa khóa truy cập tài khoản token của bạn' var apiClient = Dial (('wss://api.hbdm.com/notification trong 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}}") ``
Đội quân BeatXin vui lòng cho tôi biết, nếu websocket kết nối thị trường đăng ký, thì lấy dữ liệu thị trường bằng hàm read ((), đúng không? Nếu sử dụng exchange.GetTicker (() thì không lấy dữ liệu từ bộ nhớ cache cục bộ mà khởi động một yêu cầu rest để trả lại dữ liệu, đúng không? Chỉ có một token hỗ trợ thay đổi cách tiếp nhận thị trường thông qua exchange.IO (("websocket") và sau đó sử dụng exchange.GetTicker (()) và exchange.GetDepth (()) sẽ không yêu cầu dữ liệu từ rest của sàn giao dịch và nhận dữ liệu từ thị trường đăng ký đã nhận được tồn tại trong bộ đệm cục bộ. Tôi hiểu, đúng không?
Cỏ nhỏTheo chỉ dẫn xác minh
jsyzliuyuXin vui lòng xác nhận thông tin của FMZ Telegram Group là:
ShaltielĐược rồi.
Cỏ nhỏCó, tốt nhất là tất cả các websocket sử dụng Dial, điều khiển trực quan hơn