avatar of 发明者量化-小小梦 发明者量化-小小梦
집중하다 사신
4
집중하다
1271
수행원

프로그래밍된 거래에서 K-라인 데이터 처리에 대한 간략한 논의

만든 날짜: 2019-08-13 11:11:38, 업데이트 날짜: 2023-10-20 20:06:13
comments   8
hits   4628

프로그래밍된 거래에서 K-라인 데이터 처리에 대한 간략한 논의

프로그래밍된 거래에서 K-라인 데이터 처리에 대한 간략한 논의

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

  • ### 아이디어

예를 들어 블록체인 자산 시장인 BTC_USDT를 살펴보고 1시간을 4시간으로 합성해보겠습니다.

프로그래밍된 거래에서 K-라인 데이터 처리에 대한 간략한 논의

프로그래밍된 거래에서 K-라인 데이터 처리에 대한 간략한 논의

프로그래밍된 거래에서 K-라인 데이터 처리에 대한 간략한 논의

프로그래밍된 거래에서 K-라인 데이터 처리에 대한 간략한 논의

|시간|최고가|시가|최저가|종가| |- |- |- |- |-| |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

프로그래밍된 거래에서 K-라인 데이터 처리에 대한 간략한 논의

데이터가 일관성을 가지고 있는 것을 볼 수 있습니다.

  • ### 구현할 코드를 작성하세요

예비적인 아이디어를 검증한 후, 이 요구 사항을 예비적으로 구현하기 위한 코드 작성을 시작할 수 있습니다.

코드를 직접 공개하세요. 코드는 참조용일 뿐입니다.

    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-라인 배열 구조의 데이터를 최종적으로 반환할 수 있습니다. 다음 사항에 유의하세요.

    1. 대상 주기는 GetNewCycleRecords 함수에 데이터 원시 자료로 전달한 K-라인의 주기보다 작을 수 없습니다. 이는 작은 사이클을 이용해 더 작은 사이클의 데이터를 합성하는 것이 불가능하기 때문입니다.
    1. 설정된 목표 기간은 마감기간이어야 합니다. 사이클 폐쇄란 무엇인가요? 간단히 말해, 1시간 또는 1일 이내에 목표 주기 시간 범위가 결합되어 폐쇄 루프를 형성합니다. 예: 예를 들어, 12분 주기의 K-라인은 각 시간의 0:00에 시작합니다(예를 들어 0:00). 첫 번째 주기는 다음과 같습니다.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-라인 데이터 처리에 대한 간략한 논의

거래소 차트 비교 프로그래밍된 거래에서 K-라인 데이터 처리에 대한 간략한 논의

  • ## K-라인 데이터를 사용하여 필요한 데이터 구조를 구성합니다.

그룹 멤버들은 종종 이렇게 질문합니다: 각 K-라인의 최고 가격의 이동 평균을 계산하고 싶은데, 어떻게 해야 하나요?

일반적으로 이동평균선은 종가의 평균을 구해 이동평균선을 형성하지만, 최고가, 최저가, 시가 등을 계산해야 할 때가 있습니다. 이 시점에서는 그냥 할 수 없습니다.exchange.GetRecords() 함수로부터 반환된 K-라인 데이터는 지표 계산 함수에 직접 전달됩니다.

예를 들어: talib.MA 이동 평균 지표 계산 함수에는 두 개의 매개변수가 있습니다. 첫 번째 매개변수는 전달해야 하는 데이터이고 두 번째 매개변수는 지표 기간 매개변수입니다. 예를 들어, 우리는 다음과 같은 지표를 계산하고 싶습니다. 프로그래밍된 거래에서 K-라인 데이터 처리에 대한 간략한 논의

K-라인 주기는 4시간입니다. 거래소 차트에서는 이동평균 기간 매개변수를 9로 하여 이동평균이 설정되었습니다. 그리고 계산을 위한 데이터 소스는 각 바의 가장 높은 가격으로 설정됩니다. 프로그래밍된 거래에서 K-라인 데이터 처리에 대한 간략한 논의 즉, 이 이동 평균은 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)
      }
  }

백테스트 실행:

프로그래밍된 거래에서 K-라인 데이터 처리에 대한 간략한 논의

그림에서 마우스 위치의 이동평균 지표 값이 다음과 같은 것을 확인할 수 있습니다.11466.9289

위의 코드는 테스트를 실행하기 위한 전략에 복사할 수 있습니다. “Line Drawing Library”를 체크하고 저장하는 것을 잊지 마세요!

  • ## 디지털 화폐 시장에서 K-라인 데이터를 얻는 방법

Inventor 양적 거래 플랫폼에는 이미 exchange.GetRecords 함수라는 패키지 인터페이스가 있는데, 이를 통해 K-라인 데이터를 얻을 수 있습니다. 다음은 더 많은 K-라인을 얻기 위해 매개변수를 지정해야 하는 경우가 있으므로 데이터를 얻기 위해 교환 K-라인 데이터 인터페이스에 직접 액세스하는 것에 초점을 맞춥니다. 캡슐화된 GetRecords 인터페이스 일반적으로 100개가 반환됩니다. 전략에 처음에 100개 이상의 K-라인이 필요한 경우, 수집하고 기다려야 합니다. 전략을 가능한 한 빨리 실행하려면 함수를 직접 캡슐화하고 거래소 K-라인 인터페이스에 직접 액세스하고 매개변수를 지정하여 더 많은 K-라인 데이터를 얻을 수 있습니다.

Huobi의 BTC_USDT 거래 쌍을 예로 들면, 우리는 이 요구 사항을 구현합니다.

거래소의 API 문서를 찾아 K-line 인터페이스에 대한 설명을 확인하세요. 프로그래밍된 거래에서 K-라인 데이터 처리에 대한 간략한 논의

  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画线类库


프로그래밍된 거래에서 K-라인 데이터 처리에 대한 간략한 논의

프로그래밍된 거래에서 K-라인 데이터 처리에 대한 간략한 논의

로그에서 records.length가 300인 것을 볼 수 있는데, 이는 300개의 K-라인 데이터 레코드 막대가 있다는 것을 의미합니다. 프로그래밍된 거래에서 K-라인 데이터 처리에 대한 간략한 논의