avatar of 发明者量化-小小梦 发明者量化-小小梦
tập trung vào tin nhắn riêng tư
4
tập trung vào
1271
Người theo dõi

Dạy bạn cách thiết kế thư viện mẫu để lấy dữ liệu K-line có độ dài xác định

Được tạo ra trong: 2023-06-27 13:37:01, cập nhật trên: 2023-09-18 19:34:23
comments   0
hits   1285

Dạy bạn cách thiết kế thư viện mẫu để lấy dữ liệu K-line có độ dài xác định

Dạy bạn cách thiết kế thư viện mẫu để lấy dữ liệu K-line có độ dài xác định

Khi thiết kế một số chiến lược xu hướng, việc tính toán các chỉ số thường yêu cầu số lượng thanh K-line đủ lớn. Phụ thuộc vào API nền tảng FMZ:exchange.GetRecords()Lượng dữ liệu được cung cấp bởi hàm vàexchange.GetRecords()Đây là sự đóng gói của giao diện trao đổi K-line. Trong thiết kế ban đầu của giao diện API trao đổi tiền điện tử, không có truy vấn phân trang và giao diện K-line của sàn giao dịch chỉ cung cấp một lượng dữ liệu hạn chế, do đó nhu cầu tính toán chỉ báo với các tham số lớn hơn của một số nhà phát triển không thể đáp ứng được.

Giao diện K-line của API hợp đồng Binance hỗ trợ truy vấn phân trang. Bài viết này lấy giao diện API K-line của Binance làm ví dụ để hướng dẫn bạn cách triển khai truy vấn phân trang và chỉ định thư viện mẫu nền tảng FMZ để lấy số lượng Thanh.

Giao diện Binance K-line

Dạy bạn cách thiết kế thư viện mẫu để lấy dữ liệu K-line có độ dài xác định

Đầu tiên, bạn cần đọc tài liệu API trao đổi để xem các thông số cụ thể của giao diện. Chúng ta có thể thấy rằng khi gọi giao diện K-line này, cần phải chỉ định sản phẩm, chu kỳ K-line, phạm vi dữ liệu (thời gian bắt đầu và kết thúc), 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 dữ liệu K-line nhất định, ví dụ, để truy vấn K-line 1 giờ, đẩy từ thời gian hiện tại đến thời gian trước đó, con số là 5.000 thanh. Theo cách này, rõ ràng là bạn không thể có được dữ liệu mong muốn chỉ bằng cách gọi truy vấn giao diện API trao đổi một lần.

Sau đó, chúng ta sẽ truy vấn theo từng trang, xử lý theo từng phân đoạn từ thời điểm hiện tại đến một thời điểm nhất định trong lịch sử. Chỉ cần chúng ta biết chu kỳ của dữ liệu đường K cần thiết thì có thể dễ dàng tính toán thời gian bắt đầu và kết thúc của mỗi phân đoạn. Chỉ cần truy vấn theo hướng các khoảnh khắc lịch sử theo trình tự cho đến khi bạn tìm thấy đủ thanh. Ý tưởng này nghe có vẻ đơn giản phải không? Hãy cùng thực hiện nhé!

Thiết kế “Phiên bản JavaScript của mẫu dữ liệu lịch sử truy vấn phân trang K-line”

Chức năng giao diện mẫu thiết kế:$.GetRecordsByLength(e, period, length)

/**
 * desc: $.GetRecordsByLength 是该模板类库的接口函数,该函数用于获取指定K线长度的K线数据
 * @param {Object} e - 交易所对象
 * @param {Int} period - K线周期,秒数为单位
 * @param {Int} length - 指定获取的K线数据的长度,具体和交易所接口限制有关
 * @returns {Array<Object>} - K线数据
 */

thiết kế$.GetRecordsByLengthChức năng này thường được sử dụng ở giai đoạn đầu của hoạt động chiến lược khi cần đường K dài để tính toán chỉ báo. Sau khi thực hiện hàm này, nó sẽ thu được dữ liệu đủ dài và sau đó chỉ cần cập nhật dữ liệu K-line mới. Không cần phải gọi hàm này để lấy dữ liệu K-line siêu dài, điều này sẽ gây ra các lệnh gọi giao diện không cần thiết.

Do đó, cũng cần phải thiết kế giao diện cho các lần cập nhật dữ liệu tiếp theo:$.UpdataRecords(e, records, period)

/**
 * desc: $.UpdataRecords 是该模板类库的接口函数,该函数用于更新K线数据
 * @param {Object} e - 交易所对象
 * @param {Array<Object>} records - 需要更新的K线数据源
 * @param {Int} period - K线周期,需要和records参数传入的K线数据周期一致
 * @returns {Bool}  - 是否更新成功
 */

Bước tiếp theo là triển khai các chức năng giao diện này.

/**
 * desc: $.GetRecordsByLength 是该模板类库的接口函数,该函数用于获取指定K线长度的K线数据
 * @param {Object} e - 交易所对象
 * @param {Int} period - K线周期,秒数为单位
 * @param {Int} length - 指定获取的K线数据的长度,具体和交易所接口限制有关
 * @returns {Array<Object>} - K线数据
 */
$.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 币安期货交易所获取K线数据函数的具体实现
 * @param {Object} e - 交易所对象
 * @param {Int} period - K线周期,秒数为单位
 * @param {Int} length - 指定获取的K线数据的长度,具体和交易所接口限制有关
 * @returns {Array<Object>} - K线数据
 */
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 -> 分钟; h -> 小时; d -> 天; w -> 周; M -> 月
    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 最大值: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
        // startTime 与 endTime 之间最多只可以相差200天
        // limit 最大值: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) {
        // 计算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
        }

        // 超出交易所可查询范围,查询不到数据时
        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 是该模板类库的接口函数,该函数用于更新K线数据
 * @param {Object} e - 交易所对象
 * @param {Array<Object>} records - 需要更新的K线数据源
 * @param {Int} period - K线周期,需要和records参数传入的K线数据周期一致
 * @returns {Bool}  - 是否更新成功
 */
$.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) {
            // 添加新Bar
            records.push(r[i])
            // 更新上一个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) {
            // 更新Bar
            records[records.length - 1] = r[i]
        }
    }
    return true
}

Trong mẫu, chúng tôi chỉ triển khai hỗ trợ giao diện K-line của hợp đồng Binance, nghĩa là,getRecordsForFuturesBinanceChức năng này 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.

Kiểm tra

Bạn có thể thấy rằng không có nhiều mã trong mẫu để triển khai các chức năng này, có lẽ ít hơn 200 dòng. Sau khi viết xong mã mẫu, việc thử nghiệm là hoàn toàn cần thiết. Và để thu thập được dữ liệu như vậy, chúng ta cũng cần phải kiểm tra dữ liệu một cách chặt chẽ nhất có thể.

Bài kiểm tra yêu cầu sao chép “phiên bản JavaScript của mẫu dữ liệu lịch sử K-line truy vấn trang” và mẫu “thư viện vẽ đường thẳng” vào thư viện chiến lược của riêng bạn (trongQuảng trường Chiến lượccó thể tìm kiếm trong ). Sau đó, chúng ta tạo một chiến lược mới và kiểm tra hai mẫu này:

Dạy bạn cách thiết kế thư viện mẫu để lấy dữ liệu K-line có độ dài xác định

Dạy bạn cách thiết kế thư viện mẫu để lấy dữ liệu K-line có độ dài xác định

Dạy bạn cách thiết kế thư viện mẫu để lấy dữ liệu K-line có độ dài xác định

“Thư viện vẽ đường thẳng” được sử dụng vì chúng ta cần vẽ dữ liệu đường K thu được để quan sát.

function main() {
	LogReset(1)
	var testPeriod = PERIOD_M5
    Log("当前测试的交易所:", exchange.GetName())

    // 如果是期货则需要设置合约
    exchange.SetContractType("swap")

    // 使用$.GetRecordsByLength获取指定长度的K线数据
    var r = $.GetRecordsByLength(exchange, testPeriod, 8000)
    Log(r)

    // 使用画图测试,方便观察
    $.PlotRecords(r, "k")

    // 检测数据
    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++) {
            // 检查重复Bar
            if (i != j && r[i].Time == r[j].Time) {
                Log(r[i].Time, i, r[j].Time, j)
                throw "有重复Bar"
            }
        }
        
        // 检查Bar连续性
        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不连续"
            }            
        }
    }
    Log("检测通过")

    Log("$.GetRecordsByLength函数返回的数据长度:", r.length)

    // 更新数据
    while (true) {
        $.UpdataRecords(exchange, r, testPeriod)
        LogStatus(_D(), "r.length:", r.length)
        $.PlotRecords(r, "k")
        Sleep(5000)
    }
}

Ở đây chúng tôi sử dụngvar testPeriod = PERIOD_M5Câu này đặt chu kỳ K-line là 5 phút và chỉ định thu được 8000 ô nhịp. Sau đó chovar r = $.GetRecordsByLength(exchange, testPeriod, 8000)Giao diện trả về dữ liệu K-line dài để kiểm tra bản vẽ:

    // 使用画图测试,方便观察
    $.PlotRecords(r, "k")

Tiếp theo, chúng ta sẽ kiểm tra dữ liệu dòng K rất dài này:

    // 检测数据
    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++) {
            // 检查重复Bar
            if (i != j && r[i].Time == r[j].Time) {
                Log(r[i].Time, i, r[j].Time, j)
                throw "有重复Bar"
            }
        }
        
        // 检查Bar连续性
        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不连续"
            }            
        }
    }
    Log("检测通过")
  1. Kiểm tra xem có bất kỳ bản sao nào trong Thanh K-line không.
  2. Kiểm tra tính liên tục của Thanh K-line (xem sự khác biệt về dấu thời gian của các Thanh liền kề có bằng nhau không)

Sau khi các kiểm tra này được thông qua, hãy kiểm tra giao diện được sử dụng để cập nhật dòng K$.UpdataRecords(exchange, r, testPeriod)Bình thường:

    // 更新数据
    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 K-line trên biểu đồ chiến lược khi nó chạy trong giao dịch thực tế, để chúng ta có thể kiểm tra xem dữ liệu Thanh K-line có được cập nhật và thêm vào bình thường hay không.

Dạy bạn cách thiết kế thư viện mẫu để lấy dữ liệu K-line có độ dài xác định

Dạy bạn cách thiết kế thư viện mẫu để lấy dữ liệu K-line có độ dài xác định

Sử dụng hàm thu thập K-line hàng ngày và đặt giá trị thu thập là 8.000 (biết rằng không có dữ liệu thị trường nào trước 8.000 ngày) và thực hiện thử nghiệm vũ phu như thế này:

Dạy bạn cách thiết kế thư viện mẫu để lấy dữ liệu K-line có độ dài xác định

Chỉ có 1309 dòng hàng ngày, so với dữ liệu trên biểu đồ sàn giao dịch:

Dạy bạn cách thiết kế thư viện mẫu để lấy dữ liệu K-line có độ dài xác định

Dạy bạn cách thiết kế thư viện mẫu để lấy dữ liệu K-line có độ dài xác định

Bạn có thể thấy dữ liệu rất nhất quán.

END

Địa chỉ mẫu:「Phiên bản JavaScript của mẫu dữ liệu lịch sử K-line truy vấn phân trang」 Địa chỉ mẫu:Thư viện bản vẽ đường nét

Các mẫu và mã chiến lược trên chỉ dành cho mục đích giảng dạy và học tập. Vui lòng tối ưu hóa và sửa đổi chúng theo nhu cầu thực tế của bạn.