剥头皮(scalp)交易策略代码与回测
本章我们来介绍一个简单的剥头皮策略,本策略是自己理解了剥头皮策略的核心之后,自己写出的第一个交易策略。目前并未经过严格实测,因为本人目前采用的是趋势跟踪策略。但此策略是博主mt5自动化交易的开端,所以有着极其重要的地位。
下面是此剥头皮策略的核心思想:找到动能强劲的k线,在下一个k线小幅回调后,沿着强劲k线挂突破单,止盈止损采用固定点数,在沿着有利的方向运行一段距离后,启用移动跟踪止损。
什么是动能强劲的k线:1.k线足够大;2.如果是阳线,则其上影线很短;如果是阴线则反之;
什么是小幅回调:如果强劲k线是阳线,下一根k线回调到强劲k线高点以下一定距离(如果10pips);
什么是移动追踪止损:如果做多,价格沿着多的方向走出30pips,则激活移动止损。从此之后,止损一直保持距离价格15pips的距离
下面是剥头皮策略实现代码:
//+------------------------------------------------------------------+ //| 定义输入参数 | //+------------------------------------------------------------------+ input group "====单k线突破scalp策略======" input double tradelots=0.01; //交易手数 input ENUM_TIMEFRAMES tradeFrame = PERIOD_M5; //交易周期 input int InitSl = 150; //初始止损 input int InitTp = 150; //初始止盈 input int TriggerPoints = 30; //移动止损激活pips,0=不启用 input int TriggerSl = 15; //止损点数 input int pullbackPoints = 10; //回调多少点以上再入场 input int Magic = 223456; input group "====过滤器=====" input double minBarPoints = 90; //待突破k线最小pips,0=不启用 input int maxPinbar = 5; //做多上影线不能超过k线的几分之一,0=不启用 input int startMiniutes = 600; //开始交易分钟数 input int durationMiniutes = 240; //交易持续时间,0=不启用时间过滤
上述代码参数均有说明,我们增加了一个时间过滤器,在不活跃的时段,禁止交易。
//+------------------------------------------------------------------+ //| 全局变量 | //+------------------------------------------------------------------+ #include <Trade\Trade.mqh> CTrade trade; CPositionInfo pos; COrderInfo ord; MqlRates g_bar; //保存信号k线信息
定义一些交易中的全局变量,以便程序中使用,g_bar存储强势k线的信息。其它几个变量是操作订单的变量。
在OnInit函数中,我们设置好交易的魔幻数字
trade.SetExpertMagicNumber(Magic); trade.SetDeviationInPoints(5);
在OnTick函数中,实现整个逻辑
static int signal = 0; //信号:1-做多;-1:做空
   
   static datetime prevTime = 0;
   datetime currentTime = iTime(_Symbol, tradeFrame, 0);
   
   if(TriggerPoints>0) {
      trainStop();
   }
   
   int total = 0;
   for(int i=PositionsTotal()-1; i>=0; i--) {
      if(!pos.SelectByIndex(i) || pos.Symbol()!=_Symbol || pos.Magic()!=Magic) continue;
      total++;
   }
   
   if(total>0) {
      return;
   }
   
   if(signal != 0) {
      double ask = SymbolInfoDouble(_Symbol, SYMBOL_ASK);
      double bid = SymbolInfoDouble(_Symbol, SYMBOL_BID);
      if(ask-bid>TriggerSl) return;
      if(g_bar.close>g_bar.open) {
         if(g_bar.high-ask>pullbackPoints*_Point) {
            double entry = g_bar.high+_Point;
            double sl = entry-InitSl*_Point;
            double tp = entry+InitTp*_Point;
            trade.BuyStop(tradelots, entry, _Symbol, sl, tp, ORDER_TIME_SPECIFIED, currentTime+PeriodSeconds(tradeFrame)-1, __FILE__);
            signal = 0;
         }
      }
      if(g_bar.close<g_bar.open) {
         if(bid-g_bar.low>pullbackPoints*_Point) {
            double entry = g_bar.low+_Point;
            double sl = entry+InitSl*_Point;
            double tp = entry-InitTp*_Point;
            trade.SellStop(tradelots, entry, _Symbol, sl, tp, ORDER_TIME_SPECIFIED, currentTime+PeriodSeconds(tradeFrame)-1, __FILE__);
            signal = 0;
         }
      }
   }
   
   if(prevTime==currentTime) return;
   prevTime = currentTime;
   
   signal = 0;
   
   if(durationMiniutes>0) {   //检查是否在交易时间内
      MqlDateTime dt;
      TimeToStruct(currentTime, dt);
      if(dt.min+dt.hour*60<startMiniutes || dt.min+dt.hour*60>startMiniutes+durationMiniutes) return;
   }
   
   
   //获取前一根k线信息
   MqlRates rates[1];
   if(CopyRates(_Symbol, tradeFrame, 1, 1, rates)!=1) return;
   
   //检查是否满足信号k线要求
   if(minBarPoints>0 && rates[0].high-rates[0].low<minBarPoints*_Point) return;
   
   if(rates[0].open<rates[0].close) {//阳线
      if(maxPinbar>0 && (rates[0].high-rates[0].close)*maxPinbar>rates[0].high-rates[0].low) return;
      g_bar = rates[0];
      signal = 1;
   } else if(rates[0].open>rates[0].close) {
      if(maxPinbar>0 && (rates[0].close-rates[0].low)*maxPinbar>rates[0].high-rates[0].low) return;
      g_bar = rates[0];
      signal = -1;
   }有几个特别说明的地方:
只在当前k结束后,下一根k线开始才去检查是否是强劲k线;
同时只有一个持仓订单
进行了交易时间过滤
移动止损在每个tick上检查,以便可以及时追踪止损
下面是ontick中调用的移动止损追踪函数:
void trainStop() {
   double ask = SymbolInfoDouble(_Symbol, SYMBOL_ASK);
   double bid = SymbolInfoDouble(_Symbol, SYMBOL_BID);
   double tp,sl;
   for(int i=PositionsTotal()-1;i>=0; i--) {
      if(pos.SelectByIndex(i)) {
         if(pos.Magic()!=Magic || pos.Symbol()!=_Symbol) return;
         ulong ticket = pos.Ticket();
         
         if(pos.PositionType()==POSITION_TYPE_BUY) {
            if(bid-pos.PriceOpen()>TriggerPoints*_Point) {
               tp = NormalizeDouble(pos.TakeProfit(), _Digits);
               sl = NormalizeDouble(bid-(TriggerSl*_Point), _Digits);
               if(sl>pos.StopLoss() && sl>0) {
                  trade.PositionModify(ticket, sl, tp);
               }
            }
         }
         
         if(pos.PositionType()==POSITION_TYPE_SELL) {
            if(ask<pos.PriceOpen()-(TriggerPoints*_Point)) {
               tp = NormalizeDouble(pos.TakeProfit(), _Digits);
               sl = NormalizeDouble(ask+(TriggerSl*_Point),_Digits);
               if(sl<pos.StopLoss() && sl>0) {
                  trade.PositionModify(ticket, sl, tp);
               }
            }
         }
      }
   }
}下面是回测的参数与结果:



从上述回测结果可以看出,剥头皮策略的特点:
胜率很高,回测中达到了89%;
盈亏比差,平均盈利0.79平均亏损4.10
交易频次高,每天进行数次交易
图中靠着大量交易,8个月利润达到了惊人的150%。但这个只是回测数据,真实情况如何,还需要在实盘中测试。
另外,上述经纪商的点差很小,且没有佣金。如果你换一个点差较大且有佣金的经纪商,可能没有利润,甚至最终结果是亏损也不奇怪。
如果需要完整代码的,请联系本人免费索取。为避免纠纷,这里只介绍主要逻辑。