
При разработке некоторых трендовых стратегий расчет индикаторов часто требует достаточного количества баров K-линии. Зависит от API платформы FMZ:exchange.GetRecords()Объем данных, предоставленных функцией, иexchange.GetRecords()Это инкапсуляция интерфейса K-line обмена. На ранних этапах проектирования интерфейсов API криптовалютных бирж не было запроса на постраничную выгрузку, а интерфейс K-line биржи предоставлял лишь ограниченный объем данных, поэтому потребности некоторых разработчиков в расчетах индикаторов с большими параметрами не могли быть удовлетворены.
Интерфейс K-line API контракта Binance поддерживает постраничный запрос. В этой статье интерфейс API Binance K-line рассматривается в качестве примера, чтобы научить вас, как реализовать постраничный запрос и указать библиотеку шаблонов платформы FMZ для получения количества баров.

Во-первых, вам необходимо прочитать документацию API биржи, чтобы увидеть конкретные параметры интерфейса. Мы видим, что при вызове этого интерфейса K-line необходимо указать продукт, период K-line, диапазон данных (время начала и окончания), количество страниц и т. д.
Так как наше проектное требование заключается в запросе определенного количества данных K-line, например, для запроса 1-часовой K-line, продвиньте ее от текущего времени до прошлого, то это число составит 5000 баров. Таким образом, очевидно, что вы не сможете получить нужные вам данные, вызвав запрос интерфейса 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-линии, поскольку это приведет к ненужным вызовам интерфейса.
Поэтому необходимо также разработать интерфейс для последующего обновления данных:$.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
}
В шаблоне мы реализуем только поддержку интерфейса K-line контракта Binance, то естьgetRecordsForFuturesBinanceФункциональность также может быть расширена для поддержки интерфейсов K-line других криптовалютных бирж.
Вы можете видеть, что в шаблоне не так много кода для реализации этих функций, вероятно, менее 200 строк. После написания кода шаблона его тестирование абсолютно необходимо. И для получения таких данных нам также необходимо провести максимально тщательное тестирование.
Для теста необходимо скопировать эту «JavaScript-версию шаблона исторических данных K-line запроса страницы» и шаблон «библиотеки рисования линий» в вашу собственную библиотеку стратегий (вСтратегический квадратможно искать в ). Затем мы создаем новую стратегию и проверяем эти два шаблона:



«Библиотека рисования линий» используется, поскольку нам необходимо нарисовать полученные данные 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-line и установите сбор данных на 8000 (зная, что рыночных данных до 8000 дней нет), а затем выполните тест методом перебора следующим образом:

По сравнению с данными на биржевом графике, дневных строк всего 1309:


Вы можете видеть, что данные согласуются.
Адрес шаблона:「JavaScript-версия шаблона исторических данных K-line постраничного запроса」 Адрес шаблона:Библиотека линейных рисунков
Вышеуказанные шаблоны и коды стратегий предназначены только для целей обучения и преподавания. Пожалуйста, оптимизируйте и модифицируйте их в соответствии с вашими реальными потребностями.