Convert any k-line cycle

Author: The Little Dream, Date: 2017-02-16 18:35:13
Tags: Extent-API

Example of strategy

  • Convert the base K line to any K line cycle
  • Temporarily unsupported Second level The test version, if there are bugs, problems welcome comments.

// K线周期合成  扩展为 根据基础K线 合成 为任意周期。
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;
var isFirstFind = true;
var FirstStamp = null;

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 AssembleRecords(records, NewCycleForMS) {
    var AssRecords = records.slice(0); // 深拷贝
    var AfterAssRecords = [];
    
    if(!records || records.length < 2){
        throw (!records) ? "传入的records参数为 错误" + records : "基础K线长度小于2";
    }
    var BaseCycle = records[records.length - 1].Time - records[records.length - 2].Time;
    if(NewCycleForMS % BaseCycle !== 0){
        throw "目标周期‘" + NewCycleForMS + "’不是 基础周期 ‘" + BaseCycle + "’ 的整倍数,无法合成!";
    }
    if(NewCycleForMS / BaseCycle > records.length){
        throw "基础K线数量不足,请检查是否基础K线周期过小!";
    }

    // 判断时间戳, 找到 基础K线  相对于 目标K线的起始时间。
    var objTime = new Date();
    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); n += (NewCycleForMS / BaseCycle)) {     // 合成
        /*
        {
        Time    :一个时间戳, 精确到毫秒,与Javascript的 new Date().getTime() 得到的结果格式一样
        Open    :开盘价
        High    :最高价
        Low :最低价
        Close   :收盘价
        Volume  :交易量
        }
        */
        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 = AssRecords[n + (NewCycleForMS / BaseCycle) - 1].Volume;
        AfterAssRecords.push(cloneObj(BarObj));
    }
    
    BarObj.Time = AssRecords[n - (NewCycleForMS / BaseCycle)].Time + NewCycleForMS;  // 最后一根时间不能变,
    BarObj.Open = AssRecords[n].Open;
    BarObj.Close = AssRecords[AssRecords.length - 1].Close;
    BarObj.Volume = AssRecords[AssRecords.length - 1].Volume;
    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.High = max;
    BarObj.Low = min;
    AfterAssRecords.push(cloneObj(BarObj));

    return AfterAssRecords;
}

function main() {                                                    // 测试代码
    var records = exchange.GetRecords();
    while (!records || records.length < 24) {
        records = exchange.GetRecords();
    }
    
    // 处理界面参数,  如果写到自己的策略里面 可以参考下
    var Num_UI_NewCycleForMS = 1;
    var arrayNum = UI_NewCycleForMS.split("*");
    for(var indexNum = 0 ; indexNum < arrayNum.length ; indexNum++){
        Num_UI_NewCycleForMS = Num_UI_NewCycleForMS * Number(arrayNum[indexNum]);
    }
    Log("自定义周期毫秒时间为:", Num_UI_NewCycleForMS);
    

    while(true){
        records = _C(exchange.GetRecords);
        // Log("原始K线数据:长度", records.length, "数据:", records);
        records = AssembleRecords(records, Num_UI_NewCycleForMS);        // 第一个参数是 基础K线, 第二个参数是 要转换的周期的 毫秒数, 1000 * 60 * 20 就是 转换为 20分钟
        // Log("转换后K线数据:长度", records.length, "数据:", records);
        $.PlotRecords(records, 'BTC');
        // throw "stop"; // ceshi
        Sleep(1000);
    }
}

Related

More

bamsmenIf you synthesize the 18 hour line with the 3 hour k-line, it's fine, but the synthesis of the daylight line will be wrong.

bamsmenThe logic written in getDHM seems to have a bit of a problem, a total error: the target cycle doesn't match the base cycle!

qunxiang_wangHello, why aren't you integrating the circle in the api?

jiaozhengHello, can you solve my strategy because this is no longer retrievable

jiaozhengWhy is it that when I'm retesting with the OKEX data, the K line cycle is set to 1 minute, but the error is returned to the target? The cycle is 240000 tonnes, not the base cycle. The cycle is an integer multiple of 180000 tonnes, it can't be synthesized!

super888The code is a 4-hour K-line, which is normal for 30 minutes (the K-line in the graph is a 4-minute time interval) and abnormal for 5 minutes (it becomes a 1-hour K-line after 12 hours of the recurrence time).

The Little DreamYes, this synthesis code was written earlier, so you can read this: https://www.fmz.com/digest-topic/4154.

bamsmenThank you, I'm using this synthesis function not because I need to use a k-line cycle that the exchange doesn't provide, but because the policy requires both hourline and dayline cycles, so if you call the GetRecords function twice to get 3 hourlines and daylines respectively, will the bottom fmz layer send two network requests?

The Little DreamNow the platform directly supports custom K-line cycles, which can be used directly from the platform's functionality.

The Little DreamSome exchanges support circumscribed data, some do not, and generally packages the unified cycle; other cycles can be synthesized with smaller cycles.

The Little DreamI tested the K-line set to a 1-minute cycle, and the bottom parameter is set to 1000*60*4 which means 4 minutes, which is synthetic K-line.

jiaozhengYou can try to retrieve the data yourself, select the OKEX futures, and then set the base K line to 1 minute, then add it to 4 minutes, and you get this error. Cyclic hydrogen 240000 tons is not an integer multiple of the base cycle of hydrogen 180000 tons, it cannot be synthesized!

jiaozhengThis is my code while ((true) { records = Call ((exchange.GetRecords, PERIOD_M1)); //Log (("Primitive K-line data: length", records.length, "data:", records); records = AssembleRecords ((records, Num_UI_NewCycleForMS); // The first parameter is the base K line, the second parameter is the milliseconds of the cycle to be converted, 1000 * 60 * 20 which is converted to 20 minutes // Log (("K line data: length", records.length, "data:", records) after conversion); This is a list of all the different ways PlotRecords is credited in the database. // throw "stop"; // ceshi Sleep ((1000); I'm not going anywhere. This is the parameter setting https://dn-filebox.qbox.me/e0f51cd46827d68f42cbeffadba1c7a842fb0fb1.jpg It's clearly set to one minute, but it suggests that the basic K-line cycle is three minutes, which hasn't been the case before.

The Little DreamThe parameters you set when retesting are shown in the screenshot, or directly in the QQ group.

The Little DreamNow it's updated, and a basic cycle is required to be consistent with the target cycle. For example, the target cycle is to synthesize 6 hours, the base cycle is to be used for 1 hour, using less trouble, and to collect a lot of talent.

The Little DreamThanks for asking, I'll check the code.