
K-라인 데이터를 사용하여 프로그래밍 거래 전략을 작성할 때 일반적으로 12분 기간 K-라인 데이터, 4시간 기간 K-라인 데이터와 같이 비표준 기간 K-라인 데이터를 사용해야 하는 경우가 많습니다. 이런 비표준적인 기간을 직접 얻을 수 없습니다. 그러면 우리는 이러한 요구에 어떻게 대응할 수 있을까? 답은 분명히 방법이 있다는 것입니다. 비표준 사이클은 더 작은 사이클의 데이터를 병합하고 합성하여 얻을 수 있습니다. 여러 사이클에서 가장 높은 가격이 합성 후 가장 높은 가격으로 계산되고, 가장 낮은 가격이 합성 후 가장 낮은 가격으로 계산된다고 상상할 수 있습니다. 오프닝 가격은 변동되지 않습니다. 합성된 K-라인의 원자재 데이터의 첫 번째 시작 가격이 사용되고, 종가는 합성된 K-라인의 원자재 데이터의 마지막 종가에 해당하며, 시간은 시간입니다. 개장가를 기준으로 하며, 거래량은 원자재 데이터의 거래량을 합산하여 산출합니다. 그림에서 보는 바와 같이:
예를 들어 블록체인 자산 시장인 BTC_USDT를 살펴보고 1시간을 4시간으로 합성해보겠습니다.




|시간|최고가|시가|최저가|종가| |- |- |- |- |-| |2019.8.12 00:00|11447.07|11382.57|11367.2|11406.92| |2019.8.12 01:00|11420|11405.65|11366.6|11373.83| |2019.8.12 02:00|11419.24|11374.68|11365.51|11398.19| |2019.8.12 03:00|11407.88|11398.59|11369.7|11384.71|
이 4개의 1시간 주기의 데이터는 4시간 주기 데이터로 결합됩니다. 시가는 첫 번째 00:00 시간의 시가입니다: 11382.57 종가는 마지막 종가 즉 03:00 종가 11384.71 입니다. 가장 높은 가격은 여기 최고 가격입니다: 11447.07 가장 낮은 가격은 여기 있습니다: 11365.51 4시간 주기 시작 시간은 00:00, 1시간 K-라인 시작 시간은 2019.8.12 00:00입니다. 거래량은 1시간마다 합산될 수 있습니다(주로 가격이 어떻게 합성되는지 관찰하기 위한 것이며, 이는 거래량 데이터에 표시되지 않습니다). 여기서는 자세히 설명하지 않겠습니다.
합성된 4시간 K-라인은 다음과 같습니다. 높음: 11447.07 오픈: 11382.57 낮음: 11365.51 수신: 11384.71 시간 : 2019.8.12 00:00

데이터가 일관성을 가지고 있는 것을 볼 수 있습니다.
예비적인 아이디어를 검증한 후, 이 요구 사항을 예비적으로 구현하기 위한 코드 작성을 시작할 수 있습니다.
코드를 직접 공개하세요. 코드는 참조용일 뿐입니다.
function GetNewCycleRecords (sourceRecords, targetCycle) { // K线合成函数
var ret = []
// 首先获取源K线数据的周期
if (!sourceRecords || sourceRecords.length < 2) {
return null
}
var sourceLen = sourceRecords.length
var sourceCycle = sourceRecords[sourceLen - 1].Time - sourceRecords[sourceLen - 2].Time
if (targetCycle % sourceCycle != 0) {
Log("targetCycle:", targetCycle)
Log("sourceCycle:", sourceCycle)
throw "targetCycle is not an integral multiple of sourceCycle."
}
if ((1000 * 60 * 60) % targetCycle != 0 && (1000 * 60 * 60 * 24) % targetCycle != 0) {
Log("targetCycle:", targetCycle)
Log("sourceCycle:", sourceCycle)
Log((1000 * 60 * 60) % targetCycle, (1000 * 60 * 60 * 24) % targetCycle)
throw "targetCycle cannot complete the cycle."
}
var multiple = targetCycle / sourceCycle
var isBegin = false
var count = 0
var high = 0
var low = 0
var open = 0
var close = 0
var time = 0
var vol = 0
for (var i = 0 ; i < sourceLen ; i++) {
// 获取 时区偏移数值
var d = new Date()
var n = d.getTimezoneOffset()
if (((1000 * 60 * 60 * 24) - sourceRecords[i].Time % (1000 * 60 * 60 * 24) + (n * 1000 * 60)) % targetCycle == 0) {
isBegin = true
}
if (isBegin) {
if (count == 0) {
high = sourceRecords[i].High
low = sourceRecords[i].Low
open = sourceRecords[i].Open
close = sourceRecords[i].Close
time = sourceRecords[i].Time
vol = sourceRecords[i].Volume
count++
} else if (count < multiple) {
high = Math.max(high, sourceRecords[i].High)
low = Math.min(low, sourceRecords[i].Low)
close = sourceRecords[i].Close
vol += sourceRecords[i].Volume
count++
}
if (count == multiple || i == sourceLen - 1) {
ret.push({
High : high,
Low : low,
Open : open,
Close : close,
Time : time,
Volume : vol,
})
count = 0
}
}
}
return ret
}
// 测试
function main () {
while (true) {
var r = exchange.GetRecords() // 原始数据,作为合成K线的基础K线数据,例如要合成4小时K线,可以用1小时K线作为原始数据。
var r2 = GetNewCycleRecords(r, 1000 * 60 * 60 * 4) // 通过 GetNewCycleRecords 函数 传入 原始K线数据 r , 和目标周期, 1000 * 60 * 60 * 4 即 目标合成的周期 是4小时K线数据。
$.PlotRecords(r2, "r2") // 策略类库栏 可以勾选画线类库,调用 $.PlotRecords 画线类库 导出函数 画图。
Sleep(1000) // 每次循环间隔 1000 毫秒,防止访问K线接口获取数据过于频繁,导致交易所限制。
}
}
사실, K-라인을 합성하려면 두 가지가 필요합니다. 첫 번째는 원료 데이터, 즉 작은 주기의 K-라인 데이터입니다.var r = exchange.GetRecords()
작은 주기의 K-라인 데이터를 얻었습니다. 두 번째는 합성 기간, 즉 K-라인 데이터 합성의 목표 기간을 명확히 정의하는 것입니다.
그런 다음 GetNewCycleRecords 함수의 알고리즘을 통해 합성된 K-라인 배열 구조의 데이터를 최종적으로 반환할 수 있습니다.
다음 사항에 유의하세요.
00:00:00 ~ 00:12:00, 두 번째 사이클은00:12:00 ~ 00:24:00, 세 번째 사이클은00:24:00 ~ 00:36:00, 네 번째 사이클은00:36:00 ~ 00:48:00, 다섯 번째 사이클은00:48:00 ~ 01:00:00 , 총 1시간이 소요됩니다.13분 주기라면 오픈 사이클이고, 그러한 사이클에서 계산된 데이터는 유일하지 않습니다. 왜냐하면 합성된 데이터는 합성 데이터의 시작점에 따라 달라지기 때문입니다.
실제 디스크는 다음과 같이 실행되었습니다.

거래소 차트 비교

그룹 멤버들은 종종 이렇게 질문합니다: 각 K-라인의 최고 가격의 이동 평균을 계산하고 싶은데, 어떻게 해야 하나요?
일반적으로 이동평균선은 종가의 평균을 구해 이동평균선을 형성하지만, 최고가, 최저가, 시가 등을 계산해야 할 때가 있습니다.
이 시점에서는 그냥 할 수 없습니다.exchange.GetRecords() 함수로부터 반환된 K-라인 데이터는 지표 계산 함수에 직접 전달됩니다.
예를 들어:
talib.MA 이동 평균 지표 계산 함수에는 두 개의 매개변수가 있습니다. 첫 번째 매개변수는 전달해야 하는 데이터이고 두 번째 매개변수는 지표 기간 매개변수입니다.
예를 들어, 우리는 다음과 같은 지표를 계산하고 싶습니다.

K-라인 주기는 4시간입니다.
거래소 차트에서는 이동평균 기간 매개변수를 9로 하여 이동평균이 설정되었습니다.
그리고 계산을 위한 데이터 소스는 각 바의 가장 높은 가격으로 설정됩니다.
즉, 이 이동 평균은 9개의 4시간 K-라인 막대의 가장 높은 가격을 평균한 것이며, 이는 지표 이동 평균을 구성합니다.
직접 데이터를 구성해보고 거래소 차트에서 계산된 데이터와 동일한지 확인해 보겠습니다.
var highs = []
for (var i = 0 ; i < r2.length ; i++) {
highs.push(r2[i].High)
}
이동 평균 지표를 도출하기 위해서는 각 막대의 최고 가격의 평균을 계산해야 합니다. 그런 다음 먼저 각 막대의 최고 가격에 해당하는 각 데이터 요소를 포함하는 배열을 구성해야 합니다. highs 변수가 처음에는 빈 배열인 것을 볼 수 있고, 그런 다음 r2 캔들스틱 데이터 변수를 탐색합니다(r2를 기억하지 못하시나요? 위의 4시간 캔들스틱을 합성하는 메인 함수의 코드를 살펴보세요). r2의 각 막대의 최고 가격을 읽으세요(즉, r2[i].High, i는 0에서 r2.length - 1까지 범위에 있으며, 이를 highs로 높입니다. 이런 방식으로 K-라인 데이터 막대와 일대일로 대응하는 데이터 구조가 구성됩니다.
이 시점에서 최고치를 talib.MA 함수에 전달하여 이동 평균을 계산할 수 있습니다.
완전한 예:
function main () {
while (true) {
var r = exchange.GetRecords()
var r2 = GetNewCycleRecords(r, 1000 * 60 * 60 * 4)
if (!r2) {
continue
}
$.PlotRecords(r2, "r2") // 画出K线
var highs = []
for (var i = 0 ; i < r2.length ; i++) {
highs.push(r2[i].High)
}
var ma = talib.MA(highs, 9) // 用均线指标函数 talib.MA 计算 均线指标
$.PlotLine("high_MA9", ma[ma.length - 2], r2[r2.length - 2].Time) // 使用画线类库把均线指标画在图表上
Sleep(1000)
}
}
백테스트 실행:

그림에서 마우스 위치의 이동평균 지표 값이 다음과 같은 것을 확인할 수 있습니다.11466.9289
위의 코드는 테스트를 실행하기 위한 전략에 복사할 수 있습니다. “Line Drawing Library”를 체크하고 저장하는 것을 잊지 마세요!
Inventor 양적 거래 플랫폼에는 이미 exchange.GetRecords 함수라는 패키지 인터페이스가 있는데, 이를 통해 K-라인 데이터를 얻을 수 있습니다. 다음은 더 많은 K-라인을 얻기 위해 매개변수를 지정해야 하는 경우가 있으므로 데이터를 얻기 위해 교환 K-라인 데이터 인터페이스에 직접 액세스하는 것에 초점을 맞춥니다. 캡슐화된 GetRecords 인터페이스 일반적으로 100개가 반환됩니다. 전략에 처음에 100개 이상의 K-라인이 필요한 경우, 수집하고 기다려야 합니다. 전략을 가능한 한 빨리 실행하려면 함수를 직접 캡슐화하고 거래소 K-라인 인터페이스에 직접 액세스하고 매개변수를 지정하여 더 많은 K-라인 데이터를 얻을 수 있습니다.
Huobi의 BTC_USDT 거래 쌍을 예로 들면, 우리는 이 요구 사항을 구현합니다.
거래소의 API 문서를 찾아 K-line 인터페이스에 대한 설명을 확인하세요.

https://api.huobi.pro/market/history/kline?period=1day&size=200&symbol=btcusdt
매개변수: |매개변수 이름|유형|필수|설명|값| |-|-|-|-|-| |symbol|string|true|거래 쌍|btcusdt, ethbtc…| |period|string|true|데이터 시간 세분성, 즉 각 캔들의 시간 간격을 반환합니다.|1분, 5분, 15분, 30분, 60분, 1일, 1개월, 1주일, 1년| |size|integer|false|K-라인 데이터의 개수를 반환합니다.|[1, 2000]|
테스트 코드:
function GetRecords_Huobi (period, size, symbol) {
var url = "https://api.huobi.pro/market/history/kline?" + "period=" + period + "&size=" + size + "&symbol=" + symbol
var ret = HttpQuery(url)
try {
var jsonData = JSON.parse(ret)
var records = []
for (var i = jsonData.data.length - 1; i >= 0 ; i--) {
records.push({
Time : jsonData.data[i].id * 1000,
High : jsonData.data[i].high,
Open : jsonData.data[i].open,
Low : jsonData.data[i].low,
Close : jsonData.data[i].close,
Volume : jsonData.data[i].vol,
})
}
return records
} catch (e) {
Log(e)
}
}
function main() {
var records = GetRecords_Huobi("1day", "300", "btcusdt")
Log(records.length)
$.PlotRecords(records, "K")
}
Python 버전, Huobi 거래소 인터페이스에 접근하는 예:
#!python3
import json
import urllib2
def GetRecords_Huobi(period, size, symbol):
headers = {'User-Agent':'Mozilla/5.0 (Windows; U; Windows NT 6.1; en-US; rv:1.9.1.6) Gecko/20091201 Firefox/3.5.6'}
url = "https://api.huobi.pro/market/history/kline?" + "period=" + period + "&size=" + size + "&symbol=" + symbol
request = urllib2.Request(url)
request.add_header('User-Agent','Mozilla/5.0 (Windows; U; Windows NT 6.1; en-US; rv:1.9.1.6) Gecko/20091201 Firefox/3.5.6')
opener = urllib2.build_opener()
f= opener.open(request)
ret = f.read().decode('utf-8')
try :
jsonData = json.loads(ret)
records = []
for i in range(len(jsonData["data"]) - 1, -1, -1):
records.append({
"Time" : jsonData["data"][i]["id"] * 1000,
"High" : jsonData["data"][i]["high"],
"Open" : jsonData["data"][i]["open"],
"Low" : jsonData["data"][i]["low"],
"Close" : jsonData["data"][i]["close"],
"Volume" : jsonData["data"][i]["vol"],
})
return records
except Exception as e:
Log(e)
def main():
r = GetRecords_Huobi("1day", "300", "btcusdt")
Log(len(r))
ext.PlotRecords(r, "K") # 需要引用Python画线类库
Python 버전, Binance Exchange의 K-line 인터페이스에 액세스하는 예:
#!python3
import json
import urllib2
def GetRecords_Huobi(period, size, symbol):
headers = {'User-Agent':'Mozilla/5.0 (Windows; U; Windows NT 6.1; en-US; rv:1.9.1.6) Gecko/20091201 Firefox/3.5.6'}
url = "https://api.binance.com/api/v3/klines?symbol=" + symbol + "&interval=" + period
request = urllib2.Request(url)
request.add_header('User-Agent','Mozilla/5.0 (Windows; U; Windows NT 6.1; en-US; rv:1.9.1.6) Gecko/20091201 Firefox/3.5.6')
opener = urllib2.build_opener()
f= opener.open(request)
ret = f.read().decode('utf-8')
try :
jsonData = json.loads(ret)
records = []
for i in range(len(jsonData)):
records.append({
"Time" : float(jsonData[i][0]),
"High" : float(jsonData[i][2]),
"Open" : float(jsonData[i][1]),
"Low" : float(jsonData[i][3]),
"Close" : float(jsonData[i][4]),
"Volume" : float(jsonData[i][5]),
})
return records
except Exception as e:
Log(e)
def main():
r = GetRecords_Huobi("1m", "300", "BTCUSDT")
Log(len(r))
ext.PlotRecords(r, "K") # 需要引用Python画线类库


로그에서 records.length가 300인 것을 볼 수 있는데, 이는 300개의 K-라인 데이터 레코드 막대가 있다는 것을 의미합니다.
