海龟量化工具-海龟原版E-比率计算

Author: 道长, Date: 2021-03-04 15:02:44
Tags:


/*backtest
start: 2018-01-01 00:00:00
end: 2021-03-04 00:00:00
period: 1d
basePeriod: 1h
exchanges: [{"eid":"OKEX","currency":"BTC_USDT"}]
*/

/**
 * ATR计算
 * @param records
 * @private
 */
function _ATR(records, period) {
     var acount = 0;
     var len = records.length;

     for (var i = len-1; i >= len-period; i--) {
          var decide = Math.abs(records[i].High - records[i].Low);
          acount += decide;
     }

     return (acount / period);
}

/**
 * 生成从minNum到maxNum的随机数
 * @param minNum
 * @param maxNum
 * @return {number}
 */
function randomNum(minNum,maxNum){
     switch(arguments.length){
          case 1:
               return parseInt(Math.random()*minNum+1,10);
          case 2:
               return parseInt(Math.random()*(maxNum-minNum+1)+minNum,10);
          default:
               return 0;
     }
}

/**
 * 查询时间间隔
 * @param periodTime
 * @param periodType
 */
function getTimeInter(periodTime, periodType) {
     if("day" == periodType) {
          return (1000 * 60 * 60 * 24) * parseInt(periodTime);
     }else if("hour" == periodType) {
          return (1000 * 60 * 60) * parseInt(periodTime);
     }else {
          return (1000 * 60) * parseInt(periodTime);
     }
}

/**
 * E比率入市信号记录:每次开仓时调用
 * @direction "BUY"-做多; "SELL":做空
 * @param kPeriod 入市时atr计算所使用的K线周期
 *        atrPeriod入市时atr计算选择的时间范围
 *        kPeriod = PERIOD_D1; atrPeriod = 20 表示计算过去20天每天的平均波幅
 * @private
 */
function _Enter_Market_Record(ex, price, direction, kPeriod, atrPeriod) {
     // 计算ATR
     var records = ex.GetRecords(kPeriod);
     var atr = _ATR(records, atrPeriod);

     var now = new Date().getTime();
     var enterMarketRecord = {"enterId" : randomNum(1000000, 9999999), "date" : now, "dateStr": _D(now), "price" : price, "direction" : direction, "atr" : atr};
     Log("入市点:" + JSON.stringify(enterMarketRecord));

     var enterMarketRecords = _G("enterMarketRecords");
     if(enterMarketRecords == null) {
          enterMarketRecords = new Array();
     }
     enterMarketRecords.push(enterMarketRecord);

     _G("enterMarketRecords", enterMarketRecords);
}

/**
 * E比率计算:
 * @param periodTime=1;periodType=day 表示计算入市后1天的E比率
 *        periodTime=1;periodType=hour 表示计算入市后1小时的E比率
 *        periodTime=1;periodType=min 表示计算入市后1分钟的E比率
 * @private
 */
function _E(periodTime, periodType, ex) {
     var enterMarketRecords = _G("enterMarketRecords");

     if (enterMarketRecords != null && enterMarketRecords.length > 0) {

          var ticker = ex.GetTicker();
          var currPrice = parseFloat(ticker.Last);

          //计算设定周期的时间间隔
          var timeInter = getTimeInter(periodTime, periodType);
          // 当前时间节点
          var now = new Date().getTime();

          // 每时每刻都记录每个入市点之后一段时间内的最高点和最低点(通过K线获取的时间范围有误差)
          for (var i=0; i<enterMarketRecords.length; i++) {
               var enterMarketRec = enterMarketRecords[i];
               var inter = now - parseInt(enterMarketRec.date);
               if (inter < timeInter) {
                    // 记录入市点后的最高点和最低点
                    var high_low = _G((enterMarketRec.enterId+""));
                    if (high_low == null) {
                         var high = currPrice > enterMarketRec.price ? currPrice : enterMarketRec.price;
                         var low = currPrice < enterMarketRec.price ? currPrice : enterMarketRec.price;

                         high_low = {"highest" : high, "lowest" : low};
                         _G((enterMarketRec.enterId+""), high_low);
                    } else {
                         var high = currPrice > high_low.highest ? currPrice : high_low.highest;
                         var low = currPrice < high_low.lowest ? currPrice : high_low.lowest;

                         high_low = {"highest" : high, "lowest" : low};
                         _G((enterMarketRec.enterId+""), high_low);
                    }
                    //Log("high_low" + JSON.stringify(high_low));
               }
          }

          // 每次看最开始的那个入市点是否到检测时间,到了就计算并将该入市点从队列移除
          var enterMarketRecord0 = enterMarketRecords[0];

          if (now - enterMarketRecord0.date >= timeInter) {
               // 计算MAE和MFE
               var l_h = _G((enterMarketRecord0.enterId+""));

               var mae = 0;
               var mfe = 0;
               if (enterMarketRecord0.direction == 'UP') {
                    if (l_h.highest > enterMarketRecord0.price) {
                         mae = l_h.highest - enterMarketRecord0.price;
                    }
                    if (l_h.lowest < enterMarketRecord0.price) {
                         mfe = enterMarketRecord0.price - l_h.lowest;
                    }
               }
               if (enterMarketRecord0.direction == 'DOWN') {
                    if (l_h.lowest < enterMarketRecord0.price) {
                         mae = enterMarketRecord0.price - l_h.lowest;
                    }
                    if (l_h.highest > enterMarketRecord0.price) {
                         mfe = l_h.highest - enterMarketRecord0.price;
                    }
               }

               // 将MAE和MFE分别除以入市时的ATR(根据波动性做出调整,将不同市场标准化)
               mae = parseFloat(mae/enterMarketRecord0.atr);
               mfe = parseFloat(mfe/enterMarketRecord0.atr);

               // 计算平均MAE和MFE并记录
               var mae_avg = _G("mae_avg");
               var mfe_avg = _G("mfe_avg");

               if (mae_avg == null) {
                    mae_avg = {"mae":mae, "times":1};
               } else {
                    mae = parseFloat((mae_avg.mae + mae)/2);
                    mae_avg = {"mae":mae, "times":mae_avg.times+1};
               }
               if (mfe_avg == null) {
                    mfe_avg = {"mfe":mfe, "times":1};
               } else {
                    mfe = parseFloat((mfe_avg.mfe + mfe)/2);
                    mfe_avg = {"mfe":mfe, "times":mfe_avg.times+1};
               }

               var E_ = parseFloat(mae_avg.mae/mfe_avg.mfe);
               Log("E-比率=" + E_ + "计算最新入市点:" + JSON.stringify(enterMarketRecord0) + "后,[总入市点数量]" + mfe_avg.times + ",[记录的高低点]" + JSON.stringify(l_h) + "#ff0000");

               // 记录计算的MAE和MFE并将该入市点从队列清除
               _G("mae_avg", mae_avg);
               _G("mfe_avg", mfe_avg);
               enterMarketRecords.shift();

               // 重新加入入市点之后再记录最高最低点
               _G((enterMarketRecord0.enterId+""), null);
               _G((enterMarketRecord0.enterId+""), null);
          }
     }
}

/**
 * 使用唐奇安通道突破+趋势过滤器组合判断入市点,并使用工具方法计算E-率
 * 这里只简单测上涨变下跌和下跌变上涨的入市点,并不是完全的唐奇安突破入市
 */
function tqaTest(ex) {
     // 获取过去20天的最高和最低点
     var records = ex.GetRecords(PERIOD_D1);
     var highest = TA.Highest(records, 20, 'High');
     var lowest = TA.Lowest(records, 20, 'Low');
     //Log("highest:" + highest + ",lowest:" + lowest);

     var ticker = ex.GetTicker();
     var currPrice = parseFloat(ticker.Last);

     var times = 0;

     if (currPrice > highest || currPrice < lowest) {
          var ma50 = TA.MA(records, 50);
          var ma300 = TA.MA(records, 300);

          var lastDirection = _G("lastDirection");

          if (currPrice > highest && ma50[ma50.length-1] > ma300[ma300.length-1]) {
               // 开始做多,记录入市点
               if (lastDirection == null || lastDirection == "DOWN") {
                    _Enter_Market_Record(ex, currPrice, "UP", PERIOD_D1, 20);
                    _G("lastDirection", "UP");
               }
          }
          if (currPrice < lowest && ma50[ma50.length-1] < ma300[ma300.length-1]) {
               // 开始做空,记录入市点
               if (lastDirection == null || lastDirection == "UP"){
                    _Enter_Market_Record(ex, currPrice, "DOWN", PERIOD_D1, 20);
                    _G("lastDirection", "DOWN");
               }
          }
     }

     //计算E70:入市后70天内的E-比率,要在while(true)循环里调用
     _E(70, "day", ex);

}

function main() {

     while (true) {

          tqaTest(exchanges[0]);

          Sleep(500);
     }
}

More