2017年1114日に更新されました a. Open が検索できない問題,前回のK線に問題があるため,レコード配列へのアクセス・クロスボアリングが原因である. 更新日: 2017年11月13日 a. 開始時間の誤ったK線組み合わせをフィルタリングする b. 時間間隔の誤ったK線組み合わせをフィルタリングする 更新日:2017年6月22日 a. RecordsManagerでは,異なるK線を区別するためのName参数を追加しました. b. Fix 固定数K行の周期が1つの新しいK行の周期に満たないとき,Timeが計算が誤った問題
更新日:2017年5月31日 a. 修正 Volume 計算の誤り
原則:
機能:
制限:
この関数は, $.RecordsManager ((NewCycleMS, Name) は,新しいサイクルマネージャーを作成しました. NewCycleMSは,新しいK線周期のミリ秒数.デフォルト ((1000)6060*2) 2時間 Name: このK線を管理する名前指定 K線管理者に戻る $.AssembleRecords (レコード,BaseCycleMS) records: 取得したオリジナルレコード BaseCycleMSは固定K線周期のミリ秒で,デフォルトで固定レコードで計算する. 新しいK線周期のレコードを返します. $.GetRecordsTable ((n) は,新しいK行の最新のN項目を取得し,すべての項目をデフォルトで出力します.出力はLogStatusの出力に便利な table型です. 基本的な情報を得るために,
/*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); } }
ゼロザキK線のタイミングが間違っています ありがとうございました. この4時間の周期で得られるデータは以下の通りです. `` 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 フォローする `` 7日間周期で,次のデータが出ています: `` 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 詳細はこちら `` コード: `` main ((() {の関数について これは5分です. const period_M5 = 1000*60*5 について この数値は, const period_H4 = 1000*60*60*4 この数値は, 周回線は const period_D7 = 1000*60*60*24*7 この数値は, const period = [ 変数について period_M5 は, period_H4 は, 周期_D7 ありがとうございました. 文字列を表示します. const tables = periods.map ((period=>{) について let records = ゼロです if (period>period_D1) { となるように records = exchange.GetRecords ((PERIOD_D1)) について ログ (日経レベル, records.length) } else if (period>period_H1) { 任意の時間帯に records = exchange.GetRecords ((PERIOD_H1)) について ログ (時間軸レベル, records.length) { else } { ほかに } records = exchange.GetRecords ((PERIOD_M1)) について Log (分行レベル, records.length) ありがとうございました. const recordManager = $.RecordsManager ((period) は,このリストのリストを表示しています. const newRecords = recordManager.AssembleRecords ((records, period) 記録,期間) 記録,記録,記録,記録,記録,記録,記録,記録,記録,記録 const table = recordManager.GetRecordsTable (コンストテーブル = レコードマネージャー.Getレコードテーブル) return table 返却テーブル (笑) LogStatus ((`\` ${JSON.stringify(tables) }\``) スリープ ((60*1000) ありがとうございました. ありがとうございました. ``
グイグイ17f211 行, isFirstFind を関数内に宣言した場合else の部分は削除できます. isFirstFind が true になるたびに,最初のブランチが入力されるたびに,Log で確認できます.
ジャオチェンTypeError: undefined at main (__FILE__:525) のプロパティ 'Time' を読み取れない 基本的には,実行した1日以内にエラーが表示され,生成されたK線がTimeを見つけられない場合もあります.
中本
中本
ザルカヒックス復元が早期に開始されたことを確認したので,コードにログインしてみてください.
グイグイ17f計算の重複を避けるためでしょうか? しかし,外に出ると,間もなく新しいデータが得られなければ,計算の誤りが生じるのでしょうか?
中本
中本
ザルカヒックスmain:98:14 - TypeError: Cannot read property 'length' of null / main:242:57 TypeError: cannot read property 'Time' of undefined 定義されていないプロパティの長さを読み取れない この問題も起こります
中本
ジャオチェン解りません.コードのほとんどは,何が問題なのかはわかりませんが,小夢の原稿の合成テンプレートは,うまく使えます.
中本