Atualizado em 20171114 a. Resolver o problema de não encontrar o Open, causado pelo acesso ao limite da matriz de registros, causado pelo acesso ao limite causado por um problema na linha K anterior. Atualizado em 20171113 a. Filtrar combinações de K-strings com tempo de início incorreto b. Filtrar combinações incorretas de K-strings com intervalos de tempo Atualizado em 20170622 a. O RecordsManager adiciona o parâmetro Nome para distinguir as diferentes linhas K. b. Fix Problema de cálculo incorreto de Time quando o número fixo de K linhas não atende a um novo ciclo de K linhas
Atualizado em 20170531 a. correção erro de cálculo do volume
Princípio:
Funções:
Restrições:
Função de saída: $.RecordsManager (NewCycleMS, Name) cria um novo administrador de ciclos NewCycleMS para milissegundos de um novo ciclo da linha K. Default ((10006060 x 2) 2 horas. Name: Especifique um nome para o gerenciamento da linha K Voltar para o administrador da linha K $.AssembleRecords (records, BaseCycleMS) Records: Records originais obtidos BaseCycleMS para milissegundos de ciclo fixo de K linhas, calculado por defeito com registros fixos Retornar registros de novos ciclos de linhas K $.GetRecordsTable(n) obtém as últimas N entradas da nova linha K. Por padrão, todas as entradas são exportadas. $$.Get***** para obter algumas informações básicas
/*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); } }
ZerozakiO tempo da linha K não está certo. O que é isso? O ciclo de quatro horas é: Não sei. 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 Não sei. O ciclo de 7 dias é o seguinte: Não sei. 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 Não sei. Código: Não sei. função main() { - 5 minutos. const period_M5 = 1000 * 60 * 5 - 4 horas. const period_H4 = 1000 vezes 60 vezes 60 vezes 4 // circunferência const period_D7 = 1000 vezes 60 vezes 60 vezes 24 vezes 7 const period = [ período_M5, período_H4, período_D7, Não. enquanto (true) { const tables = periods.map ((period=>{ Let records = null Não se (período>período_D1) { registros = exchange.GetRecords ((PERIOD_D1) Log (Length, records.length) } else if (period>period_H1) { registros = exchange.GetRecords ((PERIOD_H1) Log (("nível da linha do tempo", records.length) } else { registros = exchange.GetRecords ((PERIOD_M1) Log (minuto linha nível, records.length) Não. const recordManager = $.RecordsManager (período) const newRecords = recordManager.AssembleRecords (records, period) const table = recordManager.GetRecordsTable (em inglês) retorno da tabela Não. LogStatus ((`\`${JSON.stringify(tables) }\``) Sleep ((60 * 1000) Não. Não. Não sei.
Guigui17fA partir da linha 211, a parte "else" pode ser removida se a declaração isFirstFind estiver dentro da função, pois cada vez que o isFirstFind é verdadeiro, cada vez que o isFirstFind é inserido é o primeiro ramo, e você pode fazer o Log para ver.
XiaoxiaoluTypeError: não pode ler a propriedade 'Time' de undefined at main (__FILE__:525) Basicamente, no dia em que você está rodando, esse tipo de erro é interrompido, e as linhas K geradas às vezes não encontram o Time.
O que é?const newRecords = recordManager.AssembleRecords (records, period) Não deveria ser o período. Não deveria ser escrito (caso contrário, o programa usaria recordes para calcular o intervalo de K linhas), ou escrever o intervalo de tempo de K linhas em records.
O que é?const newRecords = recordManager.AssembleRecords (records, period) Não deveria ser o período, deveria ser o número de milissegundos no intervalo de tempo de uma linha de K em um registro.
zrcahyxEu mesmo confirmei que a regressão foi causada por um início prematuro do tempo, e sugiro que você faça um log no código.
Guigui17fMas se deixarmos de fora, se não tivermos novos dados durante algum tempo, isso pode causar erros de cálculo?
O que é?Pode me dizer quais são os parâmetros dados no RecordsManager e no AssemblyRecords para que eu possa depurar?
O que é?É verdade que, como você disse, eu ainda não entendi a lógica de Little Dream, então eu pesquisei e modifiquei.
zrcahyxmain:98:14 - TypeError: Cannot read property 'length' of null / main:242:57 TypeError: cannot read property 'Time' of undefined Não é possível ler a propriedade 'length' de null / main:242:57 O problema é o mesmo.
O que é?Bem, eu tenho tempo para ver se o código é alterado, e se houver mais algum problema, vamos dizer a linha original K e a linha nova K, e eu vou ver se posso reproduzir.
XiaoxiaoluNão sei, a maior parte do código não sabe onde está o problema, mas a versão original do template sintético do Dream Theater funciona como deve ser.
O que é?O que é que a tua linha K nativa e a nova linha K estão a fazer?