Khi thiết kế các chiến lược xu hướng, thường cần có một số lượng đủ các thanh K-line để tính toán các chỉ số.exchange.GetRecords()
Trong thiết kế ban đầu của các API trao đổi tiền điện tử, không có hỗ trợ để trang trong giao diện K-line, và giao diện K-line của trao đổi chỉ cung cấp một lượng dữ liệu hạn chế. Kết quả là, một số nhà phát triển không thể đáp ứng các yêu cầu để tính toán các chỉ số với giá trị tham số lớn hơn.
Giao diện K-line của API hợp đồng của Binance hỗ trợ pagination. Trong bài viết này, chúng tôi sẽ sử dụng giao diện Binance K-line API làm ví dụ để dạy bạn cách thực hiện pagination và chỉ định số thanh để lấy bằng thư viện mẫu nền tảng FMZ.
Dữ liệu đường K
Thời gian mở của mỗi đường K trongGET /dapi/v1/klines
điểm cuối có thể được coi là một ID duy nhất.
Trọng lượng của yêu cầu phụ thuộc vào giá trị của tham số
Các thông số:
Đầu tiên, chúng ta cần tham khảo tài liệu API của Exchange để hiểu các thông số cụ thể của giao diện K-line. Chúng ta có thể thấy rằng khi gọi điểm cuối K-line này, chúng ta cần xác định loại, khoảng thời gian K-line, phạm vi dữ liệu (thời gian bắt đầu và kết thúc), và số trang, v.v.
Vì yêu cầu thiết kế của chúng tôi là truy vấn một số lượng cụ thể dữ liệu đường K, ví dụ, truy vấn đường K 1 giờ, 5000 thanh dữ liệu đường K 1 giờ từ thời điểm hiện tại sang quá khứ, rõ ràng rằng thực hiện một cuộc gọi API duy nhất đến trao đổi sẽ không lấy lại dữ liệu mong muốn.
Để đạt được điều này, chúng ta có thể thực hiện pagination và chia truy vấn thành các phân đoạn từ thời điểm hiện tại đến một thời điểm lịch sử cụ thể. Vì chúng ta biết khoảng thời gian dữ liệu K-line mong muốn, chúng ta có thể dễ dàng tính toán thời gian bắt đầu và kết thúc cho mỗi phân đoạn. Sau đó chúng ta có thể truy vấn từng phân đoạn theo trình tự hướng tới thời điểm lịch sử cho đến khi chúng ta lấy đủ thanh. Cách tiếp cận nghe có vẻ đơn giản, vì vậy hãy tiếp tục và thực hiện nó!
Chức năng giao diện cho mẫu thiết kế:$.GetRecordsByLength(e, period, length)
.
/**
* desc: $.GetRecordsByLength is the interface function of this template library, this function is used to get the K-line data of the specified K-line length
* @param {Object} e - exchange object
* @param {Int} period - K-line period, in seconds
* @param {Int} length - Specify the length of the acquired K-line data, which is related to the exchange interface limits
* @returns {Array<Object>} - K-line data
*/
Thiết kế chức năng$.GetRecordsByLength
, thường được sử dụng trong giai đoạn đầu của việc thực hiện chiến lược để tính toán các chỉ số dựa trên một khoảng thời gian dài dữ liệu đường K. Một khi chức năng này được thực hiện và có đủ dữ liệu, chỉ cần cập nhật dữ liệu đường K mới. Không cần gọi chức năng này một lần nữa để lấy dữ liệu đường K quá dài, vì nó sẽ dẫn đến các cuộc gọi API không cần thiết.
Do đó, cũng cần thiết phải thiết kế một giao diện cho các cập nhật dữ liệu tiếp theo:$.UpdataRecords(e, records, period)
.
/**
* desc: $.UpdataRecords is the interface function of this template library, this function is used to update the K-line data.
* @param {Object} e - exchange object
* @param {Array<Object>} records - K-line data sources that need to be updated
* @param {Int} period - K-line period, needs to be the same as the K-line data period passed in the records parameter
* @returns {Bool} - Whether the update was successful
*/
Bước tiếp theo là thực hiện các chức năng giao diện này.
/**
* desc: $.GetRecordsByLength is the interface function of this template library, this function is used to get the K-line data of the specified K-line length
* @param {Object} e - exchange object
* @param {Int} period - K-line period, in seconds
* @param {Int} length - Specify the length of the acquired K-line data, which is related to the exchange interface limits
* @returns {Array<Object>} - K-line data
*/
$.GetRecordsByLength = function(e, period, length) {
if (!Number.isInteger(period) || !Number.isInteger(length)) {
throw "params error!"
}
var exchangeName = e.GetName()
if (exchangeName == "Futures_Binance") {
return getRecordsForFuturesBinance(e, period, length)
} else {
throw "not support!"
}
}
/**
* desc: getRecordsForFuturesBinance, the specific implementation of the function to get K-line data for Binance Futures Exchange
* @param {Object} e - exchange object
* @param {Int} period - K-line period, in seconds
* @param {Int} length - Specify the length of the acquired K-line data, which is related to the exchange interface limits
* @returns {Array<Object>} - K-line data
*/
function getRecordsForFuturesBinance(e, period, length) {
var contractType = e.GetContractType()
var currency = e.GetCurrency()
var strPeriod = String(period)
var symbols = currency.split("_")
var baseCurrency = ""
var quoteCurrency = ""
if (symbols.length == 2) {
baseCurrency = symbols[0]
quoteCurrency = symbols[1]
} else {
throw "currency error!"
}
var realCt = e.SetContractType(contractType)["instrument"]
if (!realCt) {
throw "realCt error"
}
// m -> minute; h -> hour; d -> day; w -> week; M -> month
var periodMap = {}
periodMap[(60).toString()] = "1m"
periodMap[(60 * 3).toString()] = "3m"
periodMap[(60 * 5).toString()] = "5m"
periodMap[(60 * 15).toString()] = "15m"
periodMap[(60 * 30).toString()] = "30m"
periodMap[(60 * 60).toString()] = "1h"
periodMap[(60 * 60 * 2).toString()] = "2h"
periodMap[(60 * 60 * 4).toString()] = "4h"
periodMap[(60 * 60 * 6).toString()] = "6h"
periodMap[(60 * 60 * 8).toString()] = "8h"
periodMap[(60 * 60 * 12).toString()] = "12h"
periodMap[(60 * 60 * 24).toString()] = "1d"
periodMap[(60 * 60 * 24 * 3).toString()] = "3d"
periodMap[(60 * 60 * 24 * 7).toString()] = "1w"
periodMap[(60 * 60 * 24 * 30).toString()] = "1M"
var records = []
var url = ""
if (quoteCurrency == "USDT") {
// GET https://fapi.binance.com /fapi/v1/klines symbol , interval , startTime , endTime , limit
// limit maximum value:1500
url = "https://fapi.binance.com/fapi/v1/klines"
} else if (quoteCurrency == "USD") {
// GET https://dapi.binance.com /dapi/v1/klines symbol , interval , startTime , endTime , limit
// The difference between startTime and endTime can be up to 200 days.
// limit maximum value:1500
url = "https://dapi.binance.com/dapi/v1/klines"
} else {
throw "not support!"
}
var maxLimit = 1500
var interval = periodMap[strPeriod]
if (typeof(interval) !== "string") {
throw "period error!"
}
var symbol = realCt
var currentTS = new Date().getTime()
while (true) {
// Calculate limit
var limit = Math.min(maxLimit, length - records.length)
var barPeriodMillis = period * 1000
var rangeMillis = barPeriodMillis * limit
var twoHundredDaysMillis = 200 * 60 * 60 * 24 * 1000
if (rangeMillis > twoHundredDaysMillis) {
limit = Math.floor(twoHundredDaysMillis / barPeriodMillis)
rangeMillis = barPeriodMillis * limit
}
var query = `symbol=${symbol}&interval=${interval}&endTime=${currentTS}&limit=${limit}`
var retHttpQuery = HttpQuery(url + "?" + query)
var ret = null
try {
ret = JSON.parse(retHttpQuery)
} catch(e) {
Log(e)
}
if (!ret || !Array.isArray(ret)) {
return null
}
// When the data cannot be searched because it is beyond the searchable range of the exchange
if (ret.length == 0 || currentTS <= 0) {
break
}
for (var i = ret.length - 1; i >= 0; i--) {
var ele = ret[i]
var bar = {
Time : parseInt(ele[0]),
Open : parseFloat(ele[1]),
High : parseFloat(ele[2]),
Low : parseFloat(ele[3]),
Close : parseFloat(ele[4]),
Volume : parseFloat(ele[5])
}
records.unshift(bar)
}
if (records.length >= length) {
break
}
currentTS -= rangeMillis
Sleep(1000)
}
return records
}
/**
* desc: $.UpdataRecords is the interface function of this template library, this function is used to update the K-line data.
* @param {Object} e - exchange object
* @param {Array<Object>} records - K-line data sources that need to be updated
* @param {Int} period - K-line period, needs to be the same as the K-line data period passed in the records parameter
* @returns {Bool} - Whether the update was successful
*/
$.UpdataRecords = function(e, records, period) {
var r = e.GetRecords(period)
if (!r) {
return false
}
for (var i = 0; i < r.length; i++) {
if (r[i].Time > records[records.length - 1].Time) {
// Add a new Bar
records.push(r[i])
// Update the previous Bar
if (records.length - 2 >= 0 && i - 1 >= 0 && records[records.length - 2].Time == r[i - 1].Time) {
records[records.length - 2] = r[i - 1]
}
} else if (r[i].Time == records[records.length - 1].Time) {
// Update Bar
records[records.length - 1] = r[i]
}
}
return true
}
Trong mẫu, chúng tôi đã thực hiện hỗ trợ cho Binance hợp đồng tương lai giao diện K-line, tức là,getRecordsForFuturesBinance
Nó cũng có thể được mở rộng để hỗ trợ giao diện K-line của các sàn giao dịch tiền điện tử khác.
Như bạn có thể thấy, mã để thực hiện các chức năng này trong mẫu không rộng rãi, tổng cộng ít hơn 200 dòng. Sau khi viết mã mẫu, kiểm tra là rất quan trọng và không nên bỏ qua. Hơn nữa, để lấy dữ liệu như thế này, điều quan trọng là phải tiến hành kiểm tra kỹ lưỡng.
Để kiểm tra nó, bạn cần phải sao chép cả
function main() {
LogReset(1)
var testPeriod = PERIOD_M5
Log("Current exchanges tested:", exchange.GetName())
// If futures, you need to set up a contract
exchange.SetContractType("swap")
// Get K-line data of specified length using $.GetRecordsByLength
var r = $.GetRecordsByLength(exchange, testPeriod, 8000)
Log(r)
// Use the Plot test for easy observation
$.PlotRecords(r, "k")
// Test data
var diffTime = r[1].Time - r[0].Time
Log("diffTime:", diffTime, " ms")
for (var i = 0; i < r.length; i++) {
for (var j = 0; j < r.length; j++) {
// Check the repeat bar
if (i != j && r[i].Time == r[j].Time) {
Log(r[i].Time, i, r[j].Time, j)
throw "With duplicate Bar"
}
}
// Check Bar continuity
if (i < r.length - 1) {
if (r[i + 1].Time - r[i].Time != diffTime) {
Log("i:", i, ", diff:", r[i + 1].Time - r[i].Time, ", r[i].Time:", r[i].Time, ", r[i + 1].Time:", r[i + 1].Time)
throw "Bar discontinuity"
}
}
}
Log("Test passed")
Log("The length of the data returned by the $.GetRecordsByLength function:", r.length)
// Update data
while (true) {
$.UpdataRecords(exchange, r, testPeriod)
LogStatus(_D(), "r.length:", r.length)
$.PlotRecords(r, "k")
Sleep(5000)
}
}
Ở đây, chúng ta sử dụng đườngvar testPeriod = PERIOD_M5
sau đó chúng ta có thể thực hiện một thử nghiệm phác thảo trên các dữ liệu K-line dài được trả về bởi cácvar r = $.GetRecordsByLength(exchange, testPeriod, 8000)
interface.
// Use the plot test for easy observation
$.PlotRecords(r, "k")
Thử nghiệm tiếp theo cho dữ liệu đường K dài là:
// Test data
var diffTime = r[1].Time - r[0].Time
Log("diffTime:", diffTime, " ms")
for (var i = 0; i < r.length; i++) {
for (var j = 0; j < r.length; j++) {
// Check the repeat Bar
if (i != j && r[i].Time == r[j].Time) {
Log(r[i].Time, i, r[j].Time, j)
throw "With duplicate Bar"
}
}
// Check Bar continuity
if (i < r.length - 1) {
if (r[i + 1].Time - r[i].Time != diffTime) {
Log("i:", i, ", diff:", r[i + 1].Time - r[i].Time, ", r[i].Time:", r[i].Time, ", r[i + 1].Time:", r[i + 1].Time)
throw "Bar discontinuity"
}
}
}
Log("Test passed")
Sau khi vượt qua các kiểm tra này, kiểm tra xem giao diện được sử dụng để cập nhật dữ liệu đường K,$.UpdateRecords(exchange, r, testPeriod)
, đang hoạt động chính xác.
// Update data
while (true) {
$.UpdataRecords(exchange, r, testPeriod)
LogStatus(_D(), "r.length:", r.length)
$.PlotRecords(r, "k")
Sleep(5000)
}
Mã này sẽ liên tục xuất dữ liệu đường K trên biểu đồ chiến lược trong giao dịch trực tiếp, cho phép chúng tôi kiểm tra xem các cập nhật và bổ sung dữ liệu đường K có hoạt động đúng không.
Sử dụng dữ liệu K-line hàng ngày, chúng tôi thiết lập nó để lấy 8000 thanh (biết rằng không có dữ liệu thị trường có sẵn cho 8000 ngày trước đây).
Nhìn thấy rằng chỉ có 1309 dòng K hàng ngày, so sánh dữ liệu trên biểu đồ trao đổi:
Bạn có thể thấy rằng dữ liệu cũng phù hợp.
Địa chỉ mẫu:
Mô hình và mã chiến lược ở trên chỉ dành cho việc giảng dạy và học tập, vui lòng tối ưu hóa và sửa đổi theo nhu cầu cụ thể của giao dịch trực tiếp.