
일부 트렌드 전략을 설계할 때 지표를 계산하려면 충분한 수의 K-라인 막대가 필요한 경우가 많습니다. FMZ 플랫폼 API에 따라 다릅니다:exchange.GetRecords()함수가 제공하는 데이터의 양과exchange.GetRecords()이는 교환 K-라인 인터페이스의 캡슐화입니다. 암호화폐 거래소 API 인터페이스의 초기 설계에는 페이징 쿼리가 없었고, 거래소의 K-라인 인터페이스는 제한된 양의 데이터만 제공했기 때문에 더 큰 매개변수를 사용한 지표 계산에 대한 일부 개발자의 요구를 충족시킬 수 없었습니다.
Binance 계약 API의 K-line 인터페이스는 페이지 분할 쿼리를 지원합니다. 이 문서에서는 Binance K-line API 인터페이스를 예로 들어 페이지 분할 쿼리를 구현하고 FMZ 플랫폼 템플릿 라이브러리를 지정하여 막대 수를 얻는 방법을 알려드립니다.

먼저, 인터페이스의 구체적인 매개변수를 보려면 거래소 API 문서를 읽어야 합니다. 이 K-라인 인터페이스를 호출할 때는 제품, K-라인 기간, 데이터 범위(시작 및 종료 시간), 페이지 수 등을 지정해야 한다는 것을 알 수 있습니다.
우리의 설계 요구 사항은 지정된 수의 K-라인 데이터를 쿼리하는 것이므로, 예를 들어 1시간 K-라인을 쿼리하려면 현재 시간부터 과거 시간까지 푸시해야 하며 그 개수는 5,000개입니다. 이런 방식으로는 교환 API 인터페이스 쿼리를 한 번만 호출해서 원하는 데이터를 얻을 수 없습니다.
그런 다음 페이지 단위로 쿼리를 실행하여 현재 순간부터 과거 특정 순간까지의 세그먼트 단위로 처리합니다. 필요한 K-라인 데이터의 주기를 알고 있다면 각 세그먼트의 시작 및 종료 시간을 쉽게 계산할 수 있습니다. 막대를 충분히 찾을 때까지 순서대로 역사적 순간의 방향으로 쿼리를 실행하세요. 아이디어는 간단해 보이지 않나요? 구현해 봅시다!
디자인 템플릿 인터페이스 기능:$.GetRecordsByLength(e, period, length)。
/**
* desc: $.GetRecordsByLength 是该模板类库的接口函数,该函数用于获取指定K线长度的K线数据
* @param {Object} e - 交易所对象
* @param {Int} period - K线周期,秒数为单位
* @param {Int} length - 指定获取的K线数据的长度,具体和交易所接口限制有关
* @returns {Array<Object>} - K线数据
*/
설계$.GetRecordsByLength이 기능은 일반적으로 지표를 계산하기 위해 긴 K-라인이 필요할 때 전략 운영의 초기 단계에서 사용됩니다. 이 함수를 실행하면 충분히 긴 데이터를 얻은 후 새로운 K-라인 데이터만 업데이트하면 됩니다. 매우 긴 K-라인 데이터를 얻기 위해 이 함수를 호출할 필요는 없으며, 이로 인해 불필요한 인터페이스 호출이 발생합니다.
따라서 후속 데이터 업데이트를 위한 인터페이스도 설계해야 합니다.$.UpdataRecords(e, records, period)。
/**
* desc: $.UpdataRecords 是该模板类库的接口函数,该函数用于更新K线数据
* @param {Object} e - 交易所对象
* @param {Array<Object>} records - 需要更新的K线数据源
* @param {Int} period - K线周期,需要和records参数传入的K线数据周期一致
* @returns {Bool} - 是否更新成功
*/
다음 단계는 이러한 인터페이스 기능을 구현하는 것입니다.
/**
* 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
}
템플릿에서 우리는 Binance 계약 K-line 인터페이스의 지원만 구현합니다. 즉,getRecordsForFuturesBinance기능은 확장되어 다른 암호화폐 거래소의 K-line 인터페이스도 지원할 수 있습니다.
이러한 기능을 구현하는 템플릿에는 코드가 많지 않은 것을 알 수 있습니다. 아마 200줄도 안 될 겁니다. 템플릿 코드를 작성한 후에는 테스트가 꼭 필요합니다. 그리고 이러한 데이터 수집을 위해서는 가능한 한 엄격하게 테스트해야 합니다.
테스트에서는 이 “페이지 쿼리 K-라인 기록 데이터 템플릿의 JavaScript 버전” 및 “라인 그리기 라이브러리” 템플릿을 귀하의 전략 라이브러리에 복사해야 합니다(전략 스퀘어)에서 검색할 수 있습니다. 그런 다음 새로운 전략을 만들고 다음 두 가지 템플릿을 확인합니다.



“선 그리기 라이브러리”를 사용하는 이유는 관찰을 위해 획득한 K-라인 데이터를 그려야 하기 때문입니다.
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)
}
}
여기서 우리는 사용합니다var testPeriod = PERIOD_M5이 문장은 K-라인 주기를 5분으로 설정하고 8000개의 막대를 얻도록 지정합니다. 그 다음에는var r = $.GetRecordsByLength(exchange, testPeriod, 8000)인터페이스는 그리기 테스트를 위한 긴 K-라인 데이터를 반환합니다.
// 使用画图测试,方便观察
$.PlotRecords(r, "k")
다음으로, 우리는 이 매우 긴 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("检测通过")
이러한 검사를 통과한 후 K 라인을 업데이트하는 데 사용된 인터페이스를 확인합니다.$.UpdataRecords(exchange, r, testPeriod)정상:
// 更新数据
while (true) {
$.UpdataRecords(exchange, r, testPeriod)
LogStatus(_D(), "r.length:", r.length)
$.PlotRecords(r, "k")
Sleep(5000)
}
이 코드는 실제 거래에서 실행될 때 전략 차트에 K-라인을 지속적으로 출력하여 K-라인 막대 데이터가 정상적으로 업데이트되고 추가되는지 확인할 수 있습니다.


일일 K-라인 수집 기능을 사용하여 수집을 8,000으로 설정합니다(8,000일 이전의 시장 데이터는 없다는 것을 알고 있음). 그리고 다음과 같이 무차별 대입 테스트를 수행합니다.

거래소 차트의 데이터와 비교했을 때 일일 라인은 1309개에 불과합니다.


데이터가 일관성을 가지고 있는 것을 볼 수 있습니다.
템플릿 주소:「페이지별 쿼리 K-라인 기록 데이터 템플릿의 JavaScript 버전」 템플릿 주소:선화 라이브러리
위의 템플릿과 전략 코드는 교육 및 학습 목적으로만 제공됩니다. 실제 필요에 따라 최적화하고 수정하세요.