交易危机

 找回密码
 快捷注册(禁q号)

QQ登录

只需一步,快速开始

搜索
广告位
查看: 1341|回复: 3

[止损] 【转载技术文章】如何创建自己的追踪止损

[复制链接]

352

主题

7816

积分

3

精华

高级操盘手

金钱
7816 美元
权重
224
发表于 2016-7-6 15:29 | 显示全部楼层 |阅读模式
https://www.mql5.com/zh/articles/134


简介
我认为循规蹈矩是个不错的主意,在我们开始本文的主题前,先再次定义术语“持仓”和“订单”。

持仓 - 是一种交易契约,即某种金融工具买入或卖出数量的合约。一种工具只能有一个持仓。
订单 - 经纪人买入或卖出某种金融工具的工具。订单有多种类型:市价单和挂单,以及止损订单(止损和获利)。
图 1. 持仓和订单。

图 1. 持仓和订单。

本文主要介绍持仓的追踪止损水平。对于挂单,该操作毫无意义,因为您可以直接移动到订单价格。什么时候挂单变成持仓(或持仓的一部分),本文将派上用场。
交易持仓不仅可以通过在持仓对话框中按下 "Close"(平仓)按钮来平仓(请参见图 2)。

图 2. 在持仓对话框中使用 "Close"(平仓)按钮平仓。

图 2. 在持仓对话框中使用 "Close"(平仓)按钮平仓。1 - 打开持仓上下文菜单,2 - 选择 "Close position"(平仓)
3 - 单击 "Close"(平仓)按钮。

此外,持仓还可以在价格达到预先设定的利润水平(获利)或损失水平(止损)时自动平仓。与使用 "Close"(平仓)按钮平仓不同,通过止损和获利平仓并非是通过终端(交易人员或“EA 交易”)执行,而是由经纪人完成。因此,平仓获得充分保障,不论是否连接了网络和电源。这使得对止损的使用几乎成为交易人员工作中的强制性元素。
交易人员唯一需要执行的操作就是提交订单,以便经纪人设置保护性止损。换言之,您必须在持仓上设置止损(或以该水平设置开仓)。使用终端的 "Modify"(修改)上下文菜单命令设置止损。在持仓列表中选择一个持仓,右键单击并选择 "Modify or Delete"(修改或删除)。接下来在持仓对话框中,您需要输入必要的止损水平然后单击 "Modify"(修改)(请参见图 3)。

图 3. 设置持仓的止损水平。

图 3. 设置持仓的止损水平。1 - 打开持仓上下文菜单,2 - 单击 "Modify or Delete"(修改或删除),3 - 设置值,4 - 单击 "Modify"(修改)。
持仓的止损水平和敞口水平显示在价格图表上(请参见图 4)。

图 4. 持仓和止损。水平由红色虚线标示,左边有 sl 标记。

图 4. 持仓和止损。水平由红色虚线标示,左边有 sl 标记。

您不仅能为持仓设置止损,还能定期修改该值。例如,当价格朝向获利的方向变化时,您可以向上拉动止损以减少可能的损失。此类保护性止损的拉动称之为追踪止损。
有多种追踪止损变体:您可以简单地跟随价格以给定的距离拉动止损。您无需立即移动止损,但是当持仓达到一定的获利时,它立即移动至收支平衡的水平。该变体是 MetaTrader 5 客户端的标准内置追踪止损。要使用标准追踪止损,右键单击持仓并选择 "Trailing Stop"(追踪止损)(请参见图 5)。

图 5. 在终端中启用标准追踪止损。

图 5. 在终端中启用标准追踪止损。1 - 打开持仓上下文菜单,2 - 单击 "Trailing Stop"(追踪止损),3 - 选择值(或设置值)。
设置值命令(自定义)位于上下文菜单的底部,且未在图中显示。

除了管理价格监测,追踪止损还可以基于某些技术指标展开工作。例如,基于移动平均线可不对短期价格变化作出反应;基于云图 (Ichimoku) 指标或更合适的指标;甚至还可以基于抛物线转向 (Parabolic SAR) 指标(停损并转向),虽然最初的设计并不是基于此目的。请参见图 6。

图 6. 抛物线转向指标。

图 6. 抛物线转向指标。

在 MQL4 中,追踪止损的过程编程通常作为单独的函数创建或被集成至其他函数中。例如,MetaTrader 4 包含的 MACD 示例“EA 交易”中,追踪止损功能与订单的闭市功能集成在一起:

for(cnt=0;cnt<total;cnt++)
  {
   OrderSelect(cnt,SELECT_BY_POS,MODE_TRADES);
   if(OrderType()<=OP_SELL &&         // 检查未平仓持仓
      OrderSymbol()==Symbol())        // 检查交易品种
     {
      if(OrderType()==OP_BUY)         // 买入持仓开仓
        {
         // 要平仓吗?
         if(MacdCurrent>0 && MacdCurrent<SignalCurrent && MacdPrevious>SignalPrevious &&
            MacdCurrent>(MACDCloseLevel*Point))
           {
            OrderClose(OrderTicket(),OrderLots(),Bid,3,Violet); // 平仓
            return(0); // 退出
           }
         // 检查追踪止损
         if(TrailingStop>0)
           {
            
            if(Bid-OrderOpenPrice()>Point*TrailingStop)
              {
               if(OrderStopLoss()<Bid-Point*TrailingStop)
                 {
                  OrderModify(OrderTicket(),OrderOpenPrice(),Bid-Point*TrailingStop,OrderTakeProfit(),0,Green);
                  return(0);
                 }
              }
           }
        }
      else // 转到卖出持仓
        {
         // 要平仓吗?
         if(MacdCurrent<0 && MacdCurrent>SignalCurrent &&
            MacdPrevious<SignalPrevious && MathAbs(MacdCurrent)>(MACDCloseLevel*Point))
           {
            OrderClose(OrderTicket(),OrderLots(),Ask,3,Violet); // 平仓
            return(0); // 退出
           }
         // 检查追踪止损
         if(TrailingStop>0)
           {
            
            if((OrderOpenPrice()-Ask)>(Point*TrailingStop))
              {
               if((OrderStopLoss()>(Ask+Point*TrailingStop)) || (OrderStopLoss()==0))
                 {
                  OrderModify(OrderTicket(),OrderOpenPrice(),Ask+Point*TrailingStop,OrderTakeProfit(),0,Red);
                  return(0);
                 }
              }
           }
        }
     }
  }

MQL5 作为面向对象语言,在设计“EA 交易”方面提供了更多的可能性。您可以创建多用途多功能的类,随后可简单快速地将其集成至几乎任何“EA 交易”。在这篇文章中,我们将要开发这样的类。


1. 创建追踪止损的基类
如前文所述,存在大量的追踪止损,但它们都具有共同的功能面:

确定持仓类型(方向)
确定持仓的当前止损水平
计算新的止损水平
检查是否需要更改当前的止损水平
修改持仓的止损水平
追踪止损的类型仅决定计算的止损水平的值。因此,追踪止损的基本功能性将包含基类中。对于依赖于追踪止损类型的功能性,我们将创建子类。应用至这些子类的方法将通过基类的虚拟方法实现。

由于我们计划使用技术指标,要确保它们的稳定操作,需要向它们提供周期性装置。为此,我们将使用计时器。我们还计划开启/关闭追踪止损(在将类用作手工操作的交易系统的一部分时),并使用图形对象 - 按钮来实现此操作(在将类用作辅助“EA 交易”的一部分时)。根据这些功能要求,基类将具有以下方法集:
class CTrailingStop
  {
protected:
public:
   void CTrailingStop(){};
   void ~CTrailingStop(){};
   void Init(){};                   // 初始化类
   bool StartTimer(){};             // 启动计时器
   void StopTimer(){};              // 停止计时器
   void On(){};                     // 开启追踪止损
   void Off(){};                    // 关闭追踪止损
   bool DoStoploss(){};             // 控制持仓止损水平的主函数
   void EventHandle(){};            // 处理图标时间的方法(按下按钮开启追踪止损)
   void Deinit(){};                 // 去初始化
   virtual bool Refresh(){};        // 刷新指标
   virtual void Setparameters(){};  // 设置参数和加载指标
   virtual int Trend(){};           // 由指标显示的趋势
   virtual double BuyStoploss(){};  // 买入持仓的止损值
   virtual double SellStoploss(){}; // 卖出持仓的止损值
  };

在调用 Init() 方法时,它将接收不依赖于所使用追踪止损的类型的通用参数。该方法将设置追踪止损模式,并使用一些市场参数来准备变量。

StartTimer() - 将用于启动周期性寻址指标以及将其强制性存储在终端缓存中所需的计时器。
Stoptimer() - 将用于在结束“EA 交易”的工作时停止计时器。
On() - 启用追踪止损并设置按钮为按下模式(如果使用按钮)。
Off() - 禁用追踪止损并设置按钮为未按下模式(如果使用按钮)。
DoStoploss() - 控制持仓的止损水平的主方法。
EventHandle() - 用于处理图表事件,特别是响应按下按钮和基于按钮位置开启/关闭追踪止损。
Deinit() - 在“EA 交易”结束其工作时运行,确保释放指标句柄。
Refresh() - 刷新指标值。需要该方法以在计算止损值前确定指标的当前值。同样地,该方法是独立使用的 - 它由计时器定期调用,以保持指标处于工作状态。
SetParameters() - 在您调用此方法时,它将接收指标参数,指标和指定的参数一起加载。
Trend() - 确定通过指标显示的趋势的方法。如果指标显示上行方向,该方法返回值 1;如果显示下行方向,返回值 -1。
BuyStoploss() 和 SellStoploss() 方法将返回指标计算的买入和卖出持仓止损的新值。
1.1. Init() 方法

Init() 方法是创建类的实例后调用的首个方法。它接收独立于追踪止损类型的通用参数:交易品种、时间表、追踪止损模式(按订单号或按柱)、附加或不附加指标至图表、创建或不创建按钮。接下来,它将接收按钮属性:按钮的 X 坐标、按钮的 Y 坐标、按钮颜色、按钮标题颜色。
后续工作所需的参数存储在类变量中。此外,在 Init() 方法工作时,它将确定止损所需的主要未变更市场参数:逗号后面的位数和点的值。最后,取决于追踪止损的类型,按钮名称及其标题形成。如果设置为使用按钮,将创建按钮。

我们在“保护”部分声明所有需要的变量:

protected:
string m_symbol;             // 交易品种
ENUM_TIMEFRAMES m_timeframe; // 时间框架
bool m_eachtick;             // 在每个tick上运行
bool m_indicator;            // 在图表上显示指标
bool m_button;               // 显示“开/关”按钮
int m_button_x;              // 按钮的x坐标
int m_button_y;              // 按钮的y坐标
color m_bgcolor;             // 按钮颜色
color m_txtcolor;            // 按钮标题颜色
int m_shift;                 // 柱形偏移量
bool m_onoff;                // 开关
int m_handle;                // 指标句柄
datetime m_lasttime;         // 最近一次执行追踪止损的时间
MqlTradeRequest m_request;   // 交易请求结构体
MqlTradeResult m_result;     // 交易请求结果结构体
int m_digits;                // 价格的小数点后的位数
double m_point;              // 点值
string m_objname;            // 按钮名称
string m_typename;           // 追踪止损类型名称
string m_caption;            // 按钮标题

现在我们来编写 Init() 方法本身:

//--- 追踪止损初始化方法
void Init(string             symbol,
          ENUM_TIMEFRAMES timeframe,
          bool   eachtick  =   true,
          bool   indicator =  false,
          bool   button    =  false,
          int    button_x  =      5,
          int    button_y  =     15,
          color  bgcolor   = Silver,
          color  txtcolor  =   Blue)
  {
//--- 设置参数
   m_symbol    = symbol;    // 交易品种
   m_timeframe = timeframe; // 时间框架
   m_eachtick  = eachtick;  // true - 每个tick上运行,false - 每个bar上运行一次
//--- 设置指标值应用的柱形
   if(eachtick)
     {
      m_shift=0; // 在每个tick模式下创建柱形
     }
   else
     {
      m_shift=1; // 在每个bar模式下创建柱形
     }
   m_indicator = indicator; // true - 将指标附着到图表上
   m_button    = button;    // true - 创建按钮来开/关追踪止损
   m_button_x  = button_x;  // 按钮的x坐标
   m_button_y  = button_y;  // 按钮的y坐标
   m_bgcolor   = bgcolor;   // 按钮颜色
   m_txtcolor  = txtcolor;  // 按钮标题颜色
//--- 获取不变的市场历史
   m_digits=(int)SymbolInfoInteger(m_symbol,SYMBOL_DIGITS); // 价格的小数点后的位数
   m_point=SymbolInfoDouble(m_symbol,SYMBOL_POINT);         // 点值
//--- 创建按钮名称和标题
   m_objname="CTrailingStop_"+m_typename+"_"+symbol;        //按钮名称
   m_caption=symbol+" "+m_typename+" Trailing";             // 按钮标题
//--- 填充交易请求结构体
   m_request.symbol=m_symbol;                               //准备交易请求结构体,设置交易品种
   m_request.action=TRADE_ACTION_SLTP;                      //准备交易请求结构体,设置交易操作类型
//--- 创建按钮
   if(m_button)
     {
      ObjectCreate(0,m_objname,OBJ_BUTTON,0,0,0);                 // 创建
      ObjectSetInteger(0,m_objname,OBJPROP_XDISTANCE,m_button_x); // 设置x轴坐标
      ObjectSetInteger(0,m_objname,OBJPROP_YDISTANCE,m_button_y); // 设置y轴坐标
      ObjectSetInteger(0,m_objname,OBJPROP_BGCOLOR,m_bgcolor);    // 设置背景颜色
      ObjectSetInteger(0,m_objname,OBJPROP_COLOR,m_txtcolor);     // 设置标题颜色
      ObjectSetInteger(0,m_objname,OBJPROP_XSIZE,120);            // 设置宽度
      ObjectSetInteger(0,m_objname,OBJPROP_YSIZE,15);             // 设置高度
      ObjectSetInteger(0,m_objname,OBJPROP_FONTSIZE,7);           // 设置字体大小
      ObjectSetString(0,m_objname,OBJPROP_TEXT,m_caption);        // 设置按钮标题
      ObjectSetInteger(0,m_objname,OBJPROP_STATE,false);          // 设置按钮状态,默认关闭
      ObjectSetInteger(0,m_objname,OBJPROP_SELECTABLE,false);     // 用户不能选择和移动按钮,仅能点击它
      ChartRedraw();                                              // 图标重绘
     }
//--- 设置追踪止损的状态
   m_onoff=false;                                                 // 追踪止损的状态 - 开/关,默认关
  };

您可以看到,在创建按钮名称和标题时使用了 m_typename 变量,该变量未使用任何值进行初始化。子类构造函数将分配一个值给该变量。因此,使用不同追踪止损方法,它将具有对应于所用追踪止损类型的不同的值。

1.2. StartTimer() 方法

StartTimer() 方法用于启动“EA 交易”的通用计时器。  

//--- 开启计时器
bool StartTimer()
  {
   return(EventSetTimer(1));
  };

使用计时器时,您必须将 Refresh() 方法的调用添加至 OnTimer() 函数,以周期性请求指标。

1.3. StopTimer() 方法

StopTimer() 方法用于停止“EA 交易”的计时器。  

//--- 停止计时器
void StopTimer()
  {
   EventKillTimer();
  };

使用该方法,当“EA 交易”结束工作时,该方法将停止计时器。在运行类的 Deinit() 方法时,该方法将被调用。  

1.4. On() 方法

On() 方法用于开启追踪止损。开启通过将值 true 分配给变量 m_onoff 来完成。如果按钮在类的初始化中设置为使用,则按钮按下。

//--- 开启追踪止损
void On()
  {
   m_onoff=true;
   if(m_button)
     { // 如果使用按钮,它被“按下”
      if(!ObjectGetInteger(0,m_objname,OBJPROP_STATE))
        {
         ObjectSetInteger(0,m_objname,OBJPROP_STATE,true);
        }
     }
  }

1.5. Off() 方法

Off() 方法用于关闭追踪止损。关闭通过将值 false 分配给变量 m_onoff 来完成。如果按钮在类的初始化中设置为使用,则按钮弹起。

//--- 关闭追踪止损
void Off()
  {
   m_onoff=false;
   if(m_button)
     { //如果使用按钮,它未被“按下”
      if(ObjectGetInteger(0,m_objname,OBJPROP_STATE))
        {
         ObjectSetInteger(0,m_objname,OBJPROP_STATE,false);
        }
     }
  }

1.6. EventHandle() 方法

EventHandle() 方法将从 OnChartEvent() 函数中调用,并且相应地,它将接收所有传递至 OnChartEvent() 函数的参数。

//---追踪按钮状态(开/关)的方法
void EventHandle(const int id,const long  &lparam,const double &dparam,const string &sparam)
  {
   if(id==CHARTEVENT_OBJECT_CLICK && sparam==m_objname)
     { // 有一个按钮事件
      if(ObjectGetInteger(0,m_objname,OBJPROP_STATE))
        { // 检查按钮状态
         On(); // 开
        }
      else
        {
         Off(); // 关
        }
     }
  }

如果 CHARTEVENT_OBJECT_CLICK 事件发生,且该事件的发生伴随具有 m_objname 名称的按钮(事件伴随发生的对象名称,在 sparam 变量中传递),则取决于按钮状态,On() 或 Off() 方法将执行。

1.7. Deinit() 方法

当“EA 交易”结束工作时必须调用 Deinit() 方法。该方法将停止计时器、释放指标句柄并删除按钮(如使用的话)。

//--- 去初始化的方法
void Deinit()
  {
   StopTimer();                  // 停止计时器
   IndicatorRelease(m_handle);   // 释放指标句柄
   if(m_button)
     {
      ObjectDelete(0,m_objname); // 删除按钮
      ChartRedraw();             // 重绘图表
     }
  }

Refresh()、SetParameters()、Trend()、BuyStoploss() 和 SellStoploss() 虚拟方法将在下文中讨论 - 届时我们将创建追踪止损子类。现在,我们来考虑基类的主方法 - DoStoploss() 方法。

1.8. DoStoploss() 方法

DoStoploss() 方法是主工作方法,必须针对每个订单号从 OnTick() 函数调用该方法。如果 m_onoff 变量的值为 false(追踪止损关闭),则方法立即结束工作。  
if(!m_onoff)
  {
   return(true);// 如果追踪止损被关闭了
  }

而且,如果追踪止损在按柱模式下工作,则会检查时间 - 将创建的柱的时间和函数上次成功执行的时间进行比较。如果时间匹配,则方法结束工作。

datetime tm[1];
// 在每个bar模式下,获取最近一个价格柱形的时间
if(!m_eachtick)
  {
   // 如果没能成功复制时间,结束方法,在下一个tick到来时重复
   if(CopyTime(m_symbol,m_timeframe,0,1,tm)==-1)
     {
      return(false);
     }
   // 如果柱形的时间和本方法最后执行时间一致,结束
   if(tm[0]==m_lasttime)
     {
      return(true);
     }
  }

如果追踪止损开启且时间检查通过,则方法的主要部分执行 - 指标值刷新(Refresh() 方法被调用)。

if(!Refresh())
  { // 获取指标值
   return(false);
  }

然后,取决于 Trend() 方法返回的值,买入或卖出持仓的追踪止损执行。

// 取决于指标显示的趋势,进行多种操作
switch (Trend())
  {
   // 上升趋势
   case 1:
      // 买入持仓的追踪止损代码
      break;
   // 下降趋势
   case -1:
      // 卖出持仓的追踪止损代码
      break;
  }

我们以买入持仓为例来考虑它的工作。

if(PositionSelect(m_symbol,1000))
  {   //--- 选择持仓。如果成功,那么持仓存在
   if(PositionGetInteger(POSITION_TYPE)==POSITION_TYPE_BUY)
     {//--- 如果为买入持仓

      //--- 获取买入持仓的止损值
      sl=BuyStoploss();
      //--- 找到买入持仓被允许设置止损的位置
      double minimal=SymbolInfoDouble(m_symbol,SYMBOL_BID)-m_point*SymbolInfoInteger(m_symbol,SYMBOL_TRADE_STOPS_LEVEL);
      //--- 对值进行标准化
      sl=NormalizeDouble(sl,m_digits);
      //--- 对值进行标准化
      minimal=NormalizeDouble(minimal,m_digits);
      //---如果不能在由指标获得的水平上设置止损,
      //   那么止损将被放置在离可能止损位最近的地方
      sl=MathMin(sl,minimal);
      //--- 持仓的止损值
      double possl=PositionGetDouble(POSITION_SL);
      //--- 对值进行标准化
      possl=NormalizeDouble(possl,m_digits);
      if(sl>possl)
        {//--- 如果新的止损值大于当前止损值,
         //    那么将尝试移动止损到新的水平上
         //--- 填充请求结构体
         m_request.sl=sl;
         //--- 填充请求结构体
         m_request.tp=PositionGetDouble(POSITION_TP);
         //--- 请求
         OrderSend(m_request,m_result);
         if(m_result.retcode!=TRADE_RETCODE_DONE)
           {//--- 检查请求结果
            //--- 日志错误信息
            printf("Unable to move Stop Loss of position %s, error #%I64u",m_symbol,m_result.retcode);
            //--- 未能移动止损,结束
            return(false);
           }
        }
     }
  }

如果可以选择该持仓,将检查持仓的类型。如果持仓的类型和趋势对应,我们使用 BuyStoploss() 方法获取所需的止损值(放入 sl 变量)。接下来是确定可设置止损的允许水平。如果计算的水平相较允许值过近,调整 sl 变量的值。然后我们获得止损持仓的当前值(放入 possl 变量),并将 sl 变量的值与 possl 变量的值进行比较。如果止损 id 的新值好于当前值 - 修改持仓。

修改前,填充 MqlTradeRequest 结构的 sl 和 tp 字段。m_request.sl 变量分配到所需的止损值,m_request.tp 变量分配到获利的现有值(使其保持不变)。剩下的字段在 Init() 方法执行时填充。在结构填充后,OrderSend() 函数调用。
一旦工作完成,m_result.retcode 变量的值将受到检查。如果值不等于 TRADE_RETCODE_DONE,则出于某些原因,它将无法执行 OrderSend() 函数请求的操作。同时显示含有错误数量的日志消息,且方法完成得到执行。如果 OrderSend() 函数成功执行,则记住 DoStoploss() 方法在其上完成最后工作的柱的时间。如果发生错误,即使是在按柱模式下,将在下一订单号尝试重试方法。只要成功完成工作,尝试将一直持续。

以下是 DoStopLoss() 方法的完整代码。

bool DoStoploss()
  {
//--- 如果追踪止损被关闭
   if(!m_onoff)
     {
      return(true);
     }
   datetime tm[1];
//--- 在每个bar模式下,获取最近一个柱形的时间
   if(!m_eachtick)
     {
      //--- 如果没能成功复制时间,结束方法,在下一个tick到来时重复
      if(CopyTime(m_symbol,m_timeframe,0,1,tm)==-1)
        {
         return(false);
        }
      //--- 如果柱形的时间和本方法最后执行时间一致,结束
      if(tm[0]==m_lasttime)
        {
         return(true);
        }
     }
//--- 获取指标值
   if(!Refresh())
     {
      return(false);
     }
   double sl;
//--- 取决于指标显示的趋势,进行多种操作
   switch(Trend())
     {
      //--- 上升趋势
      case 1:
         //--- 选择持仓如果成功,那么持仓存在
         if(PositionSelect(m_symbol))
           {
            //---如果为买入持仓
            if(PositionGetInteger(POSITION_TYPE)==POSITION_TYPE_BUY)
              {
               //--- 获取买入持仓的止损值
               sl=BuyStoploss();
               //--- 找到买入持仓被允许设置止损的位置
               double minimal=SymbolInfoDouble(m_symbol,SYMBOL_BID)-m_point*SymbolInfoInteger(m_symbol,SYMBOL_TRADE_STOPS_LEVEL);
               //--- 对值进行标准化
               sl=NormalizeDouble(sl,m_digits);
               //--- 对值进行标准化
               minimal=NormalizeDouble(minimal,m_digits);
               //---如果不能在由指标获得的水平上设置止损,
               //   那么止损将被放置在离可能止损位最近的地方
               sl=MathMin(sl,minimal);
               //--- 持仓的止损值
               double possl=PositionGetDouble(POSITION_SL);
               //--- 对值进行标准化
               possl=NormalizeDouble(possl,m_digits);
               //--- 如果新的止损值大于当前止损值,
               //    那么将尝试移动止损到新的水平上
               if(sl>possl)
                 {
                  //--- 填充请求结构体
                  m_request.sl=sl;
                  //--- 填充请求结构体
                  m_request.tp=PositionGetDouble(POSITION_TP);
                  //--- 请求
                  OrderSend(m_request,m_result);
                  //--- 检查请求结果
                  if(m_result.retcode!=TRADE_RETCODE_DONE)
                    {
                     //--- 日志错误信息
                     printf("Unable to move Stop Loss of position %s, error #%I64u",m_symbol,m_result.retcode);
                     //--- 未能移动止损,结束
                     return(false);
                    }
                 }
              }
           }
         break;
         //--- 下降趋势
      case -1:
         if(PositionSelect(m_symbol))
           {
            if(PositionGetInteger(POSITION_TYPE)==POSITION_TYPE_SELL)
              {
               sl=SellStoploss();
               //--- 加上点差,因为卖单是通过买价平仓的
               sl+=(SymbolInfoDouble(m_symbol,SYMBOL_ASK)-SymbolInfoDouble(m_symbol,SYMBOL_BID));
               double minimal=SymbolInfoDouble(m_symbol,SYMBOL_ASK)+m_point*SymbolInfoInteger(m_symbol,SYMBOL_TRADE_STOPS_LEVEL);
               sl=NormalizeDouble(sl,m_digits);
               minimal=NormalizeDouble(minimal,m_digits);
               sl=MathMax(sl,minimal);
               double possl=PositionGetDouble(POSITION_SL);
               possl=NormalizeDouble(possl,m_digits);
               if(sl<possl || possl==0)
                 {
                  m_request.sl=sl;
                  m_request.tp=PositionGetDouble(POSITION_TP);
                  OrderSend(m_request,m_result);
                  if(m_result.retcode!=TRADE_RETCODE_DONE)
                    {
                     printf("Unable to move Stop Loss of position %s, error #%I64u",m_symbol,m_result.retcode);
                     return(false);
                    }
                 }
              }
           }
         break;
     }
//--- 记住方法的最近一次执行时间
   m_lasttime=tm[0];
   return(true);
  }

注意买入和卖出持仓的代码的不同之处。对于卖出持仓,SellStoploss() 返回的值需要加上点差的值,因为卖出持仓是按买价进行平仓的。相应的,对于买入,止损最小水平的倒计时从卖价开始,对于卖出则是从买价开始。

现在我们已完成了追踪止损基类的创建。接下来我们创建子类。

2. 抛物线转向指标的追踪止损子类
CTrailingStop 类的虚拟方法已向您展示了子类的内容 - SetParameters()、Refresh()、Trend()、BuyStoploss()、SellStoploss() 方法以及类构造函数设置追踪止损的名称。该类将命名为 CParabolicStop。由于该类是 CTrailingStop 的一个子类,这将在它的声明中提及。

class CParabolicStop: public CTrailingStop

通过该声明,调用 CParabolicStop 类的虚拟方法将运行基类的继承方法。  

让我们从细节上考虑子类的所有方法。

2.1. CParabolicStop() 方法

评分

参与人数 1金钱 +20 收起 理由
frlin2003 + 20 我是你的粉丝!

查看全部评分

122

主题

3万

积分

23

精华

地佐更接地气

大型投行

金钱
30820 美元
权重
870
发表于 2016-7-6 15:42 来自手机 | 显示全部楼层
不懂EA的飘过。嘿嘿。

15

主题

619

积分

1

精华

贫民

初级操盘手

金钱
619 美元
权重
1
发表于 2016-7-6 15:53 | 显示全部楼层
恩,这个有效,正是我不太懂的按钮类型
想想就行,别动手,动手非君子,等着吧

24

主题

5826

积分

0

精华

高级操盘手

金钱
5826 美元
权重
41
发表于 2016-7-6 16:30 | 显示全部楼层
。。。
坚持理解,完善理解
回复

使用道具 举报

您需要登录后才可以回帖 登录 | 快捷注册(禁q号)

本版积分规则

QQ|黄金吧|黄金论坛|手机版|指标下载|非农|目录|交易危机

版权所有: ©2014-2021 fx3q.com Powered by Discuz! X3
浙ICP备: ICP14039028

浙公网安备 33011802001420号

风险提示:杠杆风险高,交易要谨慎 声明:坛友发言和回复均为个人观点,不代表论坛立场。
若有侵权请联系fx3q@qq.com删除

快速回复 返回顶部 返回列表