임의의 K 라인 주기 관리 템플릿을 변환합니다 (최근 업데이트 20180627)

저자:중부 2017-05-22 21:08:57
태그:Extent-API

2017년 11월 14일에 업데이트 되었습니다. a. Open이 찾을 수 없는 문제를 해결합니다. 이 문제는 이전 K줄의 문제로 인해 기록 배열에 대한 접근 경계가 넘어가기 때문에 발생했습니다. 2017년 11월 13일에 업데이트 되었습니다 a. 시작시간이 틀린 K줄 조합을 필터링한다 b. 시간 간격이 잘못된 K선 조합을 필터링한다 2017년 6월 22일에 업데이트 되었습니다. a. RecordsManager에서 다른 K줄을 구분하기 위해 Name 매개 변수를 추가합니다. b. Fix 고정된 K줄 수가 새로운 K줄 주기를 채우지 않을 때 Time를 계산하는 것이 잘못되는 문제

201770531로 업데이트 되었습니다. a. 수정 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이어야 합니다. 테스트 버전, 버그, 문제 등에 대해 문의드립니다.

출력 함수: $.RecordsManager ((NewCycleMS, Name) 는 새로운 사이클 관리자를 생성합니다. NewCycleMS는 새로운 K선 주기에 밀리초를 계산합니다. 기본 (<1000)6060*2) 2시간 Name: K줄 관리자를 위한 이름 지정 K 라인 관리자로 돌아가 $.AssembleRecords (기록, BaseCycleMS) 레코드: 원본 레코드 BaseCycleMS는 고정된 K선 주기의 밀리초를 기본으로 고정된 기록으로 계산합니다. 새로운 K선 주기의 레코드를 반환합니다 $.GetRecordsTable ((n) 는 새로운 K줄의 최신 N개의 항목을 얻을 수 있습니다. 모든 항목을 기본으로 출력합니다. 출력은 logStatus 출력을 용이하게 하는 table 타입입니다. $.Get*****에서 기본적인 정보를 얻을 수 있습니다.


/*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 `` 코드: `` function main (() { 5분입니다 const period_M5 = 1000*60*5 4시간입니다 const period_H4 = 1000*60*60*4 // 둘레 const period_D7 = 1000*60*60*24*7 const period = [ 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, records.length) } else if (period>period_H1) { records = exchange.GetRecords ((PERIOD_H1)) 에 의해 변경되었습니다. 로그 (시간선 레벨, records.length) } else { records = exchange.GetRecords ((PERIOD_M1)) 에 의해 기록됩니다. 로그 (minute line level, records.length) ♪ ♪ const recordManager = $.RecordsManager ((period) 에 대해 설명합니다. const newRecords = recordManager.AssembleRecords (기록, 기간) const table = 레코드 매니저.GetRecordsTable 반환 테이블 }) 로그Status ((`\`${JSON.stringify(tables) }\``) Sleep ((60*1000) ♪ ♪ ♪ ♪ ``

이17f211 행, isFirstFind을 함수 내부에서 선언한 후else 부분은 삭제할 수 있습니다. isFirstFind이 true이므로, 입력된 것은 첫 번째 가맹점이기 때문에, Log을 클릭하여 보실 수 있습니다.

조오TypeError: undefined at main의 'Time' 속성을 읽을 수 없습니다 (__FILE__:525) 기본적으로 실행 하루에 이러한 오류 보고는 중지, 생성 K 라인 때때로 시간 찾을 수 없습니다

중부 const newRecords = recordManager.AssembleRecords (기록, 기간) 이 문장은 잘못 쓰여졌죠? period가 아니어야 합니다.

중부 const newRecords = recordManager.AssembleRecords (기록, 기간) 이 문장은 잘못 쓰여졌죠? 이 문장은 'period'이 아니어야 합니다. 이 문장은 'period'이 아니어야 합니다.

zrcahyx제가 직접 확인한 바와 같이, 재검사 시작시간이 너무 이른 것 때문이라고 생각했는데,

이17f반복적인 계산을 피하기 위해서일까요? 하지만, 만약 우리가 새로운 데이터를 얻지 못한다면 계산에 오류가 생길까요?

중부 레코드 매니저 (RecordsManager) 와 어셈블리 레코드 (AseembleRecords)) 에서 어떤 파라미터들이 있는지 알려 주시겠습니까?

중부 그리고 그 당시의 논리를 이해하지 못했던 것은 사실입니다.

zrcahyxmain:98:14 - TypeError: Cannot read property 'length' of null / main:242:57 TypeError: can't read property 'Time' of undefined (타이프 에러: null / main의 길이를 읽을 수 없다) 이 문제를 해결하기 위해

중부 오, 좋습니다, 저는 코드의 변경 사항을 살펴볼 시간이 있습니다. 만약 문제가 생기면, 원래 K줄과 새로운 K줄을 다시 만들 수 있는지 보겠습니다.

조오알 수 없습니다. 대부분의 코드에서 문제가 무엇인지 알 수 없습니다. 하지만 DreamWorks의 원본 합성 템플릿은 정상적으로 작동합니다.

중부 네이티브 K 라인과 새로운 K 라인의 설정은 무엇입니까?