트렌드 전략을 설계할 때, 지표를 계산하기 위해 충분한 수의 K-라인 바가 필요합니다.exchange.GetRecords()
FMZ 플랫폼 API에서 기능, 이는 거래소의 K-라인 인터페이스에 대한 래퍼입니다. 암호화폐 거래소 API의 초기 설계에서 K-라인 인터페이스에서 페이지링에 대한 지원이 없었으며 거래소의 K-라인 인터페이스는 제한된 양의 데이터만을 제공했습니다. 결과적으로 일부 개발자는 더 큰 매개 변수 값으로 지표를 계산하는 요구 사항을 충족할 수 없었습니다.
바이낸스
K선 데이터
각 K 라인의 열 시간GET /dapi/v1/klines
엔드포인트는 고유의 ID로 간주 될 수 있습니다.
요청의 무게는
매개 변수
먼저, 우리는 K-라인 인터페이스의 특정 매개 변수를 이해하기 위해 교환의 API 문서를 참조해야합니다. 우리는이 K-라인 엔드포인트를 호출 할 때 유형, K-라인 기간, 데이터 범위 (시작 및 종료 시간) 및 페이지 수 등을 지정해야합니다.
우리의 설계 요구 사항은 특정 수의 K-라인 데이터를 검색하기 때문에, 예를 들어, 1시간 K-라인, 5000 바의 1시간 K-라인 데이터를 현재 순간부터 과거로 검색하기 위해, 교환에 단일 API 호출을 하는 것은 원하는 데이터를 검색하지 않을 것이 분명합니다.
이를 달성하기 위해, 우리는 페이지링을 구현하고 현재 순간부터 특정 역사적 시점까지 쿼리를 세그먼트로 나눌 수 있습니다. 원하는 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
*/
함수를 설계$.GetRecordsByLength
, 일반적으로 전략 실행의 초기 단계에서 K-라인 데이터의 긴 기간에 기반한 지표를 계산하는 데 사용됩니다. 이 함수가 실행되고 충분한 데이터가 획득되면 새로운 K-라인 데이터만 업데이트해야합니다. 불필요한 API 호출을 초래하기 때문에 과도하게 긴 K-라인 데이터를 검색하기 위해 이 함수를 다시 호출할 필요가 없습니다.
따라서 다음의 데이터 업데이트를 위한 인터페이스도 설계해야 합니다.$.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
*/
다음 단계는 이러한 인터페이스 기능을 구현하는 것입니다.
/**
* 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
}
템플릿에서, 우리는 단지 바이낸스 선물 계약 K-라인 인터페이스에 대한 지원을 구현, 즉,getRecordsForFuturesBinance
또한 다른 암호화폐 거래소의 K-라인 인터페이스를 지원하기 위해 확장될 수 있습니다.
보시다시피, 템플릿에 이러한 기능을 구현하는 코드는 200줄 미만으로 광범위하지 않습니다. 템플릿 코드를 작성한 후 테스트는 매우 중요하며 간과해서는 안됩니다. 또한, 이와 같은 데이터 검색을 위해 철저한 테스트를 수행하는 것이 중요합니다.
그것을 테스트하기 위해, 당신은 당신의 전략 라이브러리 (가 찾을 수 있는) 에 페이지화 질의 K-라인 역사 데이터 템플릿의
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)
}
}
여기, 우리는 선을 사용합니다.var testPeriod = PERIOD_M5
5분 K-라인 기간을 설정하고 8000바를 검색하도록 지정합니다.var r = $.GetRecordsByLength(exchange, testPeriod, 8000)
interface.
// Use the plot test for easy observation
$.PlotRecords(r, "k")
긴 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")
이 검사를 통과한 후, K-라인 데이터를 업데이트하는 인터페이스가 사용되었는지 확인합니다.$.UpdateRecords(exchange, r, testPeriod)
, 정상적으로 작동하고 있습니다.
// Update data
while (true) {
$.UpdataRecords(exchange, r, testPeriod)
LogStatus(_D(), "r.length:", r.length)
$.PlotRecords(r, "k")
Sleep(5000)
}
이 코드는 라이브 트레이딩 중에 전략 차트에 K-라인 데이터를 지속적으로 출력하여 K-라인 데이터 업데이트와 추가가 올바르게 작동하는지 확인할 수 있습니다.
매일 K-라인 데이터를 사용하여 8000 바를 검색하도록 설정합니다.
매일 1309개의 K-라인만 있는 것을 보면, 교환 차트의 데이터를 비교해보세요.
데이터도 일치한다는 것을 알 수 있습니다.
템플릿 주소:
위의 템플릿과 전략 코드는 교육 및 학습 용도로만 제공되며 라이브 거래의 특정 필요에 따라 최적화 및 수정하십시오.