Được cập nhật vào 2017 1114 a. Giải quyết vấn đề không tìm thấy Open, do truy cập vào phạm vi của mảng record, truy cập vào phạm vi của mảng K trước đó gây ra vấn đề. Được cập nhật vào 20171113 a. Xử lý kết hợp K-string không chính xác thời gian bắt đầu b. Xử lý các kết hợp K không chính xác giữa các khoảng thời gian Được cập nhật vào năm 2017622 a. RecordsManager thêm tham số Name để dễ dàng phân biệt các dòng K khác nhau b. Fix Vấn đề Time tính không chính xác khi số lượng K dây cố định không đáp ứng một chu kỳ K dây mới
Được cập nhật vào ngày 070531 a. fix Lỗi tính toán khối lượng
Nguyên tắc:
Chức năng:
Hạn chế:
Chức năng đầu ra: $.RecordsManager (NewCycleMS, Name) tạo ra một trình quản lý chu kỳ mới NewCycleMS cho phép đếm milliseconds cho chu kỳ mới của dòng K. mặc định ((10006060 x 2) 2 giờ. Name: Đặt tên cho quản lý dòng K Trở lại trình quản lý K-Line $.AssembleRecords (đăng ký, BaseCycleMS) records: Các bản ghi nguyên bản được lấy BaseCycleMS cho chu kỳ K-line cố định trong millisecond, tính toán bằng các fixedrecords mặc định Trở lại các bản ghi cho chu kỳ mới của dòng K $.GetRecordsTable(n) Nhận được N mục mới nhất của dòng K, xuất tất cả các mục theo mặc định, xuất dạng table, dễ dàng xuất LogStatus $.Get***** để có được thông tin cơ bản
/*backtest period: 60 */ /* 20180627 修改了天无法整合的bug 20180118 屏蔽掉一些log输出 更新于20171114 a. 解决Open找不到问题,是record数组访问越界造成的,访问越界是之前的K线有问题导致的。 更新于20171113 a. 过滤掉起始时间不正确的k线组合 b. 过滤掉时间间隔不正确的k线组合 更新于20170622 a. RecordsManager 增加Name参数,便于区分不同的K线 b. Fix 当固定K线数目不满一根新K线周期时,Time计算不正确的问题 更新于20170531 a. fix Volume计算错误 1. 修改自小小梦的"转换任意K线周期" 模板 原理: - 获得固定K线的周期,然后合成任意固定K线整数倍的新K线周期 功能: - 转换基础K线为任意K线周期 - 暂时不支持 秒级别 限制: - 新K线周期必须是固定K线周期的整数倍. - 固定K线周期为1min, 3min, 5min, 15min, 30min, 新K线周期也必须是分钟且<60 - 固定K线周期为1hour, 新K线周期也必须是小时且<24 - 固定K线周期为1day, 新K线周期也必须是天 - 每次获得的固定K线周期数目必须>=2 测试版本,如有BUG ,问题 欢迎留言。 输出函数: $.RecordsManager(NewCycleMS, Name) 生成新周期管理器 NewCycleMS 为新K线周期毫秒数. 默认(1000*60*60*2) 2hour. Name: 为该K线管理指定名字 返回K线管理器 $.AssembleRecords(records, BaseCycleMS) records: 拿到的原始records BaseCycleMS 为固定K线周期毫秒,默认用固定records进行计算 返回新K线周期的records $.GetRecordsTable(n) 得到新K线最新的N个条目, 默认输出所有条目, 输出为table类型,便于LogStatus输出 $.Get***** 获得一些基本信息 */ function EasyReadTime(millseconds) { if (typeof millseconds == 'undefined' || !millseconds) { millseconds = new Date().getTime(); } var newDate = new Date(); newDate.setTime(millseconds); return newDate.toLocaleString(); } var cloneObj = function(obj) { // 深拷贝 对象函数 var str, newobj = obj.constructor === Array ? [] : {}; if (typeof obj !== 'object') { return; } else if (JSON) { str = JSON.stringify(obj); //系列化对象 newobj = JSON.parse(str); //还原 } else { for (var i in obj) { newobj[i] = typeof obj[i] === 'object' ? cloneObj(obj[i]) : obj[i]; } } return newobj; }; var DAY = 0; var HOURS = 1; var MINUTES = 2; function GetDHM(objTime, BaseCycle, NewCycleForMS){ var ret = []; if(BaseCycle % (1000 * 60 * 60 * 24) === 0){ ret[0] = objTime.getDate(); ret[1] = DAY; }else if(BaseCycle % (1000 * 60 * 60) === 0){ ret[0] = objTime.getHours(); ret[1] = HOURS; }else if(BaseCycle % (1000 * 60) === 0){ ret[0] = objTime.getMinutes(); ret[1] = MINUTES; } if(NewCycleForMS % (1000 * 60 * 60 * 24) === 0){ ret[2] = DAY; }else if(NewCycleForMS % (1000 * 60 * 60) === 0){ ret[2] = HOURS; }else if(NewCycleForMS % (1000 * 60) === 0){ ret[2] = MINUTES; } return ret; } function SearchFirstTime(ret, BaseCycle, NewCycleForMS){ if(ret[1] === DAY && ret[2] === DAY){ var array_day = []; for(var i = 1 ; i < 29; i += (NewCycleForMS / BaseCycle)){ array_day.push(i); } for(var j = 0 ; j < array_day.length; j++ ){ if(ret[0] === array_day[j]){ return true; } } }else if(ret[1] === HOURS && ret[2] === HOURS){ var array_hours = []; for(var i = 0 ; i < 24; i += (NewCycleForMS / BaseCycle)){ array_hours.push(i); } for(var j = 0 ; j < array_hours.length ; j++){ if(ret[0] === array_hours[j]){ return true; } } }else if(ret[1] === MINUTES && ret[2] === MINUTES){ var array_minutes = []; for(var i = 0; i < 60; i += (NewCycleForMS / BaseCycle)){ array_minutes.push(i); } for(var j = 0; j < array_minutes.length; j++){ if(ret[0] === array_minutes[j]){ return true; } } }else{ throw "目标周期与基础周期不匹配!目标周期毫秒数:" + NewCycleForMS + " 基础周期毫秒数: " + BaseCycle; } } function Calc_High(AssRecords, n, BaseCycle, NewCycleForMS){ var max = AssRecords[n].High; for(var i = 1 ; i < NewCycleForMS / BaseCycle; i++){ max = Math.max(AssRecords[n + i].High, max); } return max; } function Calc_Low(AssRecords, n, BaseCycle, NewCycleForMS){ var min = AssRecords[n].Low; for(var i = 1 ; i < NewCycleForMS / BaseCycle; i++){ min = Math.min(AssRecords[n + i].Low, min); } return min; } function _RecordsManager(NewCycleForMS, Name) { if (typeof NewCycleForMS == 'string') { this._NewCycleForMS = 1; var arrayNum = NewCycleForMS.split("*"); for(var indexNum = 0 ; indexNum < arrayNum.length ; indexNum++){ this._NewCycleForMS = this._NewCycleForMS * Number(arrayNum[indexNum]); } } else { this._NewCycleForMS = NewCycleForMS; } this._Name = ""; if (Name) { this._Name = Name; } this._Records = new Array(); this.GetNewCycleForMS = function() { return this._NewCycleForMS; }; this.GetRecords = function() { return this._Records; } this.AssembleRecords = function(records, BaseCycle) { var NewCycleForMS = this._NewCycleForMS; var AssRecords = records.slice(0); // 深拷贝 var AfterAssRecords = []; if (!records || records.length == 0) { Log("record 为空@!"); return records; } if(records.length < 2){ throw (!records) ? "传入的records参数为 错误" + records : "基础K线长度小于2"; } if (typeof BaseCycle === 'undefined') { BaseCycle = records[records.length - 1].Time - records[records.length - 2].Time; } if(NewCycleForMS % BaseCycle !== 0){ //Log(EasyReadTime(records[records.length - 1].Time), EasyReadTime(records[records.length - 2].Time)); //Log("目标周期‘", NewCycleForMS, "’不是 基础周期 ‘", BaseCycle, "’ 的整倍数,无法合成!"); return null; } if(NewCycleForMS / BaseCycle > records.length){ Log("records: ", records, "NewCycleForMS: ", NewCycleForMS, ", BaseCycle: ", BaseCycle); throw "基础K线数量不足,请检查是否基础K线周期过小!"; } // 判断时间戳, 找到 基础K线 相对于 目标K线的起始时间。 var objTime = new Date(); var isFirstFind = true; var FirstStamp = null; for (var i = 0; i < AssRecords.length; i++) { objTime.setTime(AssRecords[i].Time); var ret = GetDHM(objTime, BaseCycle, NewCycleForMS); if (isFirstFind === true && SearchFirstTime(ret, BaseCycle, NewCycleForMS) === true) { FirstStamp = AssRecords[i].Time; for (j = 0; j < i; j++) { AssRecords.shift(); // 把目标K线周期前不满足合成的数据排除。 } isFirstFind = false; break; // 排除后跳出 }else if(isFirstFind === false){ if((AssRecords[i].Time - FirstStamp) % NewCycleForMS === 0){ for (j = 0; j < i; j++) { AssRecords.shift(); // 把目标K线周期前不满足合成的数据排除。 } break; } } } var BarObj = { // 定义一个 K线柱结构 Time: 0, Open: 0, High: 0, Low: 0, Close: 0, Volume: 0, }; var n = 0; for (n = 0; n < AssRecords.length - (NewCycleForMS / BaseCycle);) { // 合成 /* { Time :一个时间戳, 精确到毫秒,与Javascript的 new Date().getTime() 得到的结果格式一样 Open :开盘价 High :最高价 Low :最低价 Close :收盘价 Volume :交易量 } */ //时间判断 var is_bad = false; var start_time = AssRecords[n].Time; var stop_time = AssRecords[n + (NewCycleForMS / BaseCycle) - 1].Time + BaseCycle; if (ret[2] != DAY && start_time % NewCycleForMS != 0) { //Log("过滤起始时间不正确的k线组合", EasyReadTime(start_time)); is_bad = true; } if (stop_time - start_time != NewCycleForMS) { //Log("过滤时间间隔不正确的k线组合", EasyReadTime(start_time), EasyReadTime(stop_time)); is_bad=true; } if (is_bad) { n++; continue; } BarObj.Time = AssRecords[n].Time; BarObj.Open = AssRecords[n].Open; BarObj.High = Calc_High(AssRecords, n, BaseCycle, NewCycleForMS); BarObj.Low = Calc_Low(AssRecords, n, BaseCycle, NewCycleForMS); BarObj.Close = AssRecords[n + (NewCycleForMS / BaseCycle) - 1].Close; BarObj.Volume = 0; for (var j = n; j < n + (NewCycleForMS / BaseCycle); j++) { BarObj.Volume += AssRecords[j].Volume; } AfterAssRecords.push(cloneObj(BarObj)); n += (NewCycleForMS / BaseCycle) } if (n == 0) { BarObj.Time = AssRecords[0].Time; } else { BarObj.Time = AssRecords[n - (NewCycleForMS / BaseCycle)].Time + NewCycleForMS; // 最后一根时间不能变, } BarObj.Open = AssRecords[n].Open; BarObj.Close = AssRecords[AssRecords.length - 1].Close; BarObj.Volume = AssRecords[n].Volume; //BarObj.Volume = 0; var max = AssRecords[n].High; var min = AssRecords[n].Low; for(var index_n = n + 1 ;index_n < AssRecords.length; index_n++){ max = Math.max(max, AssRecords[index_n].High); min = Math.min(min, AssRecords[index_n].Low); BarObj.Volume += AssRecords[index_n].Volume; } BarObj.High = max; BarObj.Low = min; AfterAssRecords.push(cloneObj(BarObj)); this._Records = AfterAssRecords; return AfterAssRecords; }; this.GetKlineName = function () { return " " + this._NewCycleForMS / 60 / 1000 + " 分钟K线"; }; /* 获得records数据表格*/ this.GetRecordsTable = function (n) { if (typeof n !== 'undefined' && n >=0 ) { var records = this._Records.slice(-n); } else { var records = this._Records.slice(0); } var record_array = new Array(); for (var i = records.length - 1; i >= 0; i--) { var newDate = new Date(); newDate.setTime(records[i].Time); var time_str = newDate.toLocaleString(); record_array.push([time_str, records[i].Open, records[i].Close, records[i].High, records[i].Low, records[i].Volume]); } var title = this._Name + " " + this.GetKlineName() + "(" + records.length + "根)"; var table = {type: 'table', title: title, cols: ['Time', 'Open','Close', 'High', 'Low', 'Volume'], rows: record_array}; return table; } } $.RecordsManager = function (NewCycleForMS, Name) { if (typeof NewCycleForMS === 'undefined') { NewCycleForMS = UI_NewCycleForMS; } var RecordsManager = new _RecordsManager(NewCycleForMS, Name); return RecordsManager; } function main() { var records = exchange.GetRecords(); while (!records || records.length < 24) { records = exchange.GetRecords(); Sleep(1000); } while (true) { records = _C(exchange.GetRecords); record_manager0 = $.RecordsManager(UI_NewCycleForMS, "Hello World"); new_records0 = record_manager0.AssembleRecords(records); var table0 = record_manager0.GetRecordsTable(); var BaseCycle = records[records.length - 1].Time - records[records.length - 2].Time; record_manager1 = $.RecordsManager(BaseCycle); new_records1 = record_manager1.AssembleRecords(records); var table1 = record_manager1.GetRecordsTable(); LogStatus('`' + JSON.stringify([table0, table1, ""]) +'`'); records = record_manager1.GetRecords(); //Log(records[records.length-1]); Sleep(60000); } }
ZerozakiK-Line không đúng thời gian.
Không có gì đâu.
Các dữ liệu được thu được là:
``
2018-07-30 19:00:00.000+08:00 8162.42 8187.69 8200 8156.84 681.53875929
2018-07-30 12:00:00.000+08:00 8192.91 8185.3 8209.35 8151 902.81758958
2018-07-30 08:00:00.000+08:00 8213.11 8154.49 8225.18 8051 899.96507317
2018-07-30 04:00:00.000+08:00 8214.25 8191.52 8229.08 8190.03 879.26269857
2018-07-30 00:00:00.000+08:00 8234.88 8185.32 8247.36 8170.04 817.78392428
``
Những con số đã được đưa ra trong chu kỳ 7 ngày là:
``
2018-08-05 08:00:00.000+08:00 8213.11 8186.16 8278.23 8051 7869.96734215
2018-07-29 08:00:00.000+08:00 8235.84 8213.63 8301.2 8116.8 20943.43495
2018-07-28 08:00:00.000+08:00 8187.47 8236.39 8245.23 8070 21465.51463651
2018-07-27 08:00:00.000+08:00 7930.92 8188.83 8278 7800.44 22985.16836742
2018-07-26 08:00:00.000+08:00 8171.44 7936.88 8316.49 7850.88 22644.64610719
2018-07-25 08:00:00.000+08:00 8404.91 8171.44 8485 8050 22036.92873654
2018-07-24 08:00:00.000+08:00 7715.53 8404.95 8498.04 7695.05 23595.19928712
2018-07-23 08:00:00.000+08:00 7395.57 7716.12 7813 7370.26 27205.92883481
2018-07-22 08:00:00.000+08:00 7402.1 7395.14 7585.15 7330.64 26186.61248972
2018-07-21 08:00:00.000+08:00 7326.6 7404 7450 7210 28545.21532011
2018-07-20 08:00:00.000+08:00 7472.61 7326.59 7691.67 7265.14 28703.79798543
``
Mã là:
``
function main (() {
- 5 phút.
const period_M5 = 1000*60*5
// 4 giờ
const period_H4 = 1000*60*60*4
// đường viền
const period_D7 = 1000*60*60*24*7
const periods = [
period_M5,
period_H4,
period_D7,
]]
while (true) {
const tables = periods.map ((period=>{
let records = null
if (period>period_D1) {
records = exchange.GetRecords ((PERIOD_D1))
Log (đường độ ngày, records.length)
} else if (period>period_H1) {
records = exchange.GetRecords ((PERIOD_H1))
Log (("Cấp độ dòng giờ", records.length)
} else {
records = exchange.GetRecords ((PERIOD_M1))
Log (đường phút, records.length)
♪ ♪
const recordManager = $.RecordsManager ((period))
const newRecords = recordManager.AssembleRecords ((records, period))
const table = recordManager.GetRecordsTable ();
return table
Guigui17fTrong câu 211, nếu bạn tuyên bố isFirstFind bên trong hàm, bạn có thể loại bỏ phần else, bởi vì mỗi lần isFirstFind là true, và mỗi lần nhập là nhánh đầu tiên, bạn có thể nhấn Log để xem.
JiaozhengTypeError: cannot read property 'Time' of undefined at main (__FILE__:525) Không thể đọc thuộc tính 'Time' của undefined at main (__FILE__:525) Về cơ bản, một ngày chạy, báo cáo lỗi như vậy sẽ dừng lại, và đôi khi dòng K được tạo ra sẽ không tìm thấy thời gian.
Trung tâmconst newRecords = recordManager.AssembleRecords ((records, period)) Không nên viết period. Bạn có thể không viết nó (nếu không, bạn sẽ sử dụng record để tính khoảng thời gian giữa các dòng K) hoặc viết khoảng thời gian giữa các dòng K trong records.
Trung tâmconst newRecords = recordManager.AssembleRecords ((records, period)) Không phải là period. Không nên viết, hoặc viết khoảng thời gian trong K trong các record.
zrcahyxTôi tự xác nhận rằng điều này là do thời gian bắt đầu quá sớm, nên tôi khuyên bạn nên đăng nhập trong mã.
Guigui17fNhưng nếu bạn đặt nó ra ngoài nếu bạn không có dữ liệu mới trong một khoảng thời gian, nó có thể dẫn đến lỗi trong tính toán?
Trung tâmBạn có thể cho tôi một số tham số trong RecordsManager và AssemblyRecords để tôi có thể gỡ lỗi không?
Trung tâmTôi đã tìm hiểu thêm về nghiên cứu và thay đổi nó.
zrcahyxmain:98:14 - TypeError: Cannot read property 'length' of null / main:242:57 Và cũng có vấn đề này.
Trung tâmỒ, tốt, tôi có thời gian để xem các thay đổi trong mã, và nếu có vấn đề nữa, hãy nói về dòng K gốc và dòng K mới, và tôi sẽ xem liệu tôi có thể tái tạo được không.
JiaozhengKhông rõ, hầu hết các mã đều không biết vấn đề ở đâu, nhưng mẫu tổng hợp gốc của DreamHost hoạt động tốt.
Trung tâmTôi đã sử dụng nó và chạy được vài tháng, không có vấn đề.