
Khi viết các chiến lược giao dịch theo chương trình, sử dụng dữ liệu K-line, thường cần sử dụng một số dữ liệu K-line theo chu kỳ không chuẩn, chẳng hạn như dữ liệu K-line theo chu kỳ 12 phút, dữ liệu K-line theo chu kỳ 4 giờ, thường là loại không chuẩn này Khoảng thời gian không thể lấy được trực tiếp. Vậy chúng ta phản ứng thế nào trước những nhu cầu như vậy? Câu trả lời chắc chắn là có cách. Có thể thu được các chu kỳ không chuẩn bằng cách hợp nhất và tổng hợp dữ liệu từ các chu kỳ nhỏ hơn. Bạn có thể tưởng tượng rằng giá cao nhất trong nhiều chu kỳ được tính là giá cao nhất sau khi tổng hợp và giá thấp nhất được tính là giá thấp nhất sau khi tổng hợp. Mở đầu giá sẽ không thay đổi. , giá mở cửa đầu tiên của dữ liệu nguyên liệu của dòng K tổng hợp được sử dụng, giá đóng cửa tương ứng với giá đóng cửa cuối cùng của dữ liệu nguyên liệu của dòng K tổng hợp, thời gian là thời gian của giá mở cửa và khối lượng giao dịch được tính bằng cách cộng tổng khối lượng giao dịch của dữ liệu nguyên liệu thô. Như thể hiện trong hình ảnh:
Chúng tôi lấy thị trường tài sản blockchain BTC_USDT làm ví dụ và tổng hợp 1 giờ thành 4 giờ.




|Thời gian|Cao|Mở|Thấp|Đóng| |- |- |- |- |-| |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|
Dữ liệu của bốn chu kỳ 1 giờ này được kết hợp thành dữ liệu chu kỳ 4 giờ. Giá mở cửa là giá mở cửa của thời điểm 00:00 đầu tiên: 11382,57 Giá đóng cửa là giá cuối cùng, tức là giá đóng cửa lúc 03:00: 11384.71 Giá cao nhất là giá cao nhất ở đây: 11447.07 Giá thấp nhất ở đây là: 11365.51 Thời gian bắt đầu của chu kỳ 4 giờ là 00:00, thời gian bắt đầu của dòng K 1 giờ, tức là 2019.8.12 00:00 Khối lượng giao dịch có thể được tổng hợp sau mỗi 1 giờ (chủ yếu để quan sát cách giá được tổng hợp, điều này không được hiển thị trong dữ liệu khối lượng giao dịch). Tôi sẽ không đi vào chi tiết ở đây.
Đường K-line 4 giờ tổng hợp là: Cao: 11447.07 Mở: 11382.57 Thấp: 11365.51 Đã nhận: 11384.71 Thời gian: 2019.8.12 00:00

Bạn có thể thấy dữ liệu rất nhất quán.
Sau khi xác minh những ý tưởng sơ bộ, bạn có thể bắt đầu viết một số mã để triển khai sơ bộ yêu cầu này.
Phát hành code trực tiếp, code chỉ mang tính chất tham khảo:
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线接口获取数据过于频繁,导致交易所限制。
}
}
Trên thực tế, để tổng hợp K-line, cần có hai thứ. Thứ nhất là dữ liệu nguyên liệu, tức là dữ liệu K-line của một chu kỳ nhỏ.var r = exchange.GetRecords()
Dữ liệu dòng K có chu kỳ nhỏ thu được. Thứ hai là xác định rõ ràng thời kỳ tổng hợp, tức là thời kỳ mục tiêu để tổng hợp dữ liệu dòng K.
Sau đó, thông qua thuật toán của hàm GetNewCycleRecords, dữ liệu của cấu trúc mảng K-line tổng hợp cuối cùng có thể được trả về.
Cần lưu ý rằng:
00:00:00 ~ 00:12:00, chu kỳ thứ hai là00:12:00 ~ 00:24:00, chu kỳ thứ ba là00:24:00 ~ 00:36:00, chu kỳ thứ tư là00:36:00 ~ 00:48:00, chu kỳ thứ năm là00:48:00 ~ 01:00:00 , kéo dài tổng cộng 1 giờ.Nếu là chu kỳ 13 phút, thì đó là chu kỳ mở và dữ liệu được tính toán trong chu kỳ như vậy không phải là duy nhất, vì dữ liệu tổng hợp sẽ khác nhau tùy thuộc vào điểm bắt đầu của dữ liệu tổng hợp.
Đĩa thực tế đã được chạy:

So sánh biểu đồ trao đổi

Các thành viên trong nhóm thường đặt câu hỏi: Tôi muốn tính trung bình động của mức giá cao nhất của mỗi K-line thì tôi phải làm thế nào?
Thông thường, chúng ta tính toán đường trung bình động bằng cách tính trung bình giá đóng cửa để tạo thành đường trung bình động, nhưng đôi khi cần phải tính giá cao nhất, giá thấp nhất, giá mở cửa, v.v.
Vào lúc này, bạn không thể chỉexchange.GetRecords() Dữ liệu dòng K do hàm trả về được truyền trực tiếp vào hàm tính toán chỉ báo.
Ví dụ:
Hàm tính toán chỉ báo trung bình động talib.MA có hai tham số. Tham số đầu tiên là dữ liệu cần truyền vào và tham số thứ hai là tham số chu kỳ chỉ báo.
Ví dụ, chúng tôi muốn tính toán các chỉ số sau

Chu kỳ dòng K là 4 giờ.
Trên biểu đồ trao đổi, một đường trung bình động đã được thiết lập với tham số chu kỳ trung bình động là 9.
Và nguồn dữ liệu để tính toán được đặt thành mức giá cao nhất của mỗi Thanh.
Nghĩa là, đường trung bình động này là trung bình của mức giá cao nhất trong 9 thanh K-line 4 giờ, tạo nên đường trung bình động của chỉ báo.
Chúng ta hãy tự xây dựng một số dữ liệu và xem liệu nó có giống với dữ liệu được tính toán bởi biểu đồ của sàn giao dịch hay không.
var highs = []
for (var i = 0 ; i < r2.length ; i++) {
highs.push(r2[i].High)
}
Vì chúng ta cần tính toán giá trung bình cao nhất của mỗi thanh để có được chỉ báo trung bình động. Sau đó, trước tiên bạn cần xây dựng một mảng, trong đó mỗi phần tử dữ liệu tương ứng với giá cao nhất của mỗi thanh. Bạn có thể thấy rằng biến highs ban đầu là một mảng rỗng, sau đó chúng ta duyệt qua biến dữ liệu nến r2 (không nhớ r2? Hãy xem mã trong hàm chính tổng hợp nến 4 giờ ở trên). Đọc giá cao nhất của mỗi thanh r2 (tức là r2[i].High , i có phạm vi từ 0 đến r2.length - 1 ), sau đó đẩy nó lên mức highs . Theo cách này, một cấu trúc dữ liệu được xây dựng tương ứng một-một với thanh dữ liệu dòng K.
Tại thời điểm này, giá trị cao có thể được truyền vào hàm talib.MA để tính toán đường trung bình động.
Ví dụ đầy đủ:
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)
}
}
Chạy thử nghiệm ngược:

Bạn có thể thấy rằng các giá trị chỉ báo trung bình động tại vị trí chuột trong hình là11466.9289
Mã trên có thể được sao chép vào chiến lược để chạy thử nghiệm. Hãy nhớ kiểm tra “Line Drawing Library” và lưu lại!
Nền tảng giao dịch định lượng Inventor đã có một giao diện đóng gói, cụ thể là hàm exchange.GetRecords, có thể thu thập dữ liệu K-line. Sau đây tập trung vào việc truy cập trực tiếp vào giao diện dữ liệu K-line trao đổi để lấy dữ liệu, vì đôi khi bạn cần chỉ định các tham số để lấy thêm K-line, giao diện GetRecords được đóng gói Thông thường sẽ trả về 100. Nếu chiến lược ban đầu yêu cầu hơn 100 dòng K, bạn cần thu thập và chờ đợi. Để chiến lược chạy nhanh nhất có thể, bạn có thể tự đóng gói một hàm, truy cập trực tiếp vào giao diện K-line trao đổi và chỉ định các tham số để có thêm dữ liệu K-line.
Lấy cặp giao dịch BTC_USDT của Huobi làm ví dụ, chúng tôi triển khai yêu cầu này:
Tìm tài liệu API của sàn giao dịch và xem mô tả về giao diện K-line:

https://api.huobi.pro/market/history/kline?period=1day&size=200&symbol=btcusdt
tham số: |Tên tham số|Loại|Bắt buộc|Mô tả|Giá trị| |-|-|-|-|-| |biểu tượng|chuỗi|đúng|Cặp giao dịch|btcusdt, ethbtc…| |period|string|true|Trả về mức độ chi tiết của dữ liệu thời gian, tức là khoảng thời gian của mỗi nến|1 phút, 5 phút, 15 phút, 30 phút, 60 phút, 1 ngày, 1 tháng, 1 tuần, 1 năm| |size|integer|false|Trả về số lượng dữ liệu K-line|[1, 2000]|
Mã kiểm tra:
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")
}
Phiên bản Python, ví dụ về cách truy cập giao diện sàn giao dịch 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画线类库
Phiên bản Python, ví dụ về cách truy cập giao diện K-line của Binance Exchange:
#!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画线类库


Chúng ta có thể thấy trong nhật ký rằng records.length là 300, điều đó có nghĩa là có 300 thanh dữ liệu K-line.
