English Русский Deutsch 日本語
preview
让新闻交易轻松上手(第五部分):执行交易(2)

让新闻交易轻松上手(第五部分):执行交易(2)

MetaTrader 5交易系统 | 26 六月 2025, 14:35
116 6
Kabelo Frans Mampa
Kabelo Frans Mampa

概述

在本文中,我们的主要目标是编写实现止损订单的新闻交易EA,这些止损订单将在后续文章中用于交易新闻事件。此外,我们将创建用于管理止损订单滑点、平仓以及执行验证检查用于指示是否可以开仓或下单的函数。在任何算法交易系统中,交易管理至关重要,因为它涉及开仓、平仓、调整止损和管理获利等任务。高效的交易管理能够帮助交易者捕获更多利润,同时将不利的市场波动风险降至最低。


为什么使用止损订单?


限价订单与止损订单

在交易新闻事件时使用止损单是一种常见策略,因为它们有助于交易者利用重大经济数据公布后经常出现的急剧价格波动,同时还能最小化某些风险。

捕捉突破走势

新闻事件,如经济报告或央行公告,往往会引发突然且显著的价格波动。这些被称为突破走势。止损单允许交易者在价格突破某一特定阈值时,以预先设定的价格水平进入市场,这对于捕捉这些突破走势非常理想,而无需手动监控市场。

  • 买入止损单:设定在当前市场价格之上。当价格上涨时,会触发买入订单,捕捉看涨事件新闻后的上涨势头。
  • 卖出止损单:设定在当前市场价格之下。当价格下跌时,会触发卖出订单,利用看跌事件新闻带来的机会。

可能有助于避免市场假突破

与新闻相关的波动性可能导致价格在明确方向确立之前出现波动。过早入场的交易者可能会陷入假突破——即价格在两个方向上的快速波动,这样可能会触发止损。止损单通过确保交易仅在市场突破特定价格点并确认方向后才触发,有助于避免这类假突破。

减少交易中的过度分析

由于市场走势迅速,围绕新闻事件的交易可能会充满压力。提前设置止损单允许交易者无需情感干扰或无需判断事件方向即可入场和出场。一旦订单下达,交易者的决策就已经确定,消除对短期价格波动的过度反应。就本文而言,这意味着对于那些难以或无法事先预测方向的舆论和其他新闻事件,可以使用止损单进行交易。

最小化滑点

通过使用止损单,交易者可以避免在高波动性期间按市场价下单时出现的滑点。尽管滑点仍可能发生,但与新闻事件已经导致价格大幅变化后按照市场价下单相比,止损单更有可能在接近预期水平的位置执行。

风险管理

止损单有助于自动化风险管理。例如,如果交易者预期新闻事件将引发剧烈反应,他们可以同时下达买入止损单和卖出止损单(跨式策略)。这种方法有助于交易者根据价格突破的方向进入市场。一旦触发其中一个止损单,可以取消另一个止损单以管理风险。

场景:

您正在围绕非农就业人数(NFP)公告这一最具市场影响力的新闻事件交易美元。预期该数据将导致欧元兑美元货币对出现大幅波动,您在当前价格之上设置了买入止损单,在当前价格之下设置了卖出止损单(跨式策略)。

NFP公告前的快照

  • 正向NFP数据:美元走强,导致欧元兑美元下跌,触发您的卖出止损单。
  • 负面NFP数据:美元走弱,导致欧元兑美元上涨,触发您的买入止损单。

NFP公告后的快照

通过使用止损单,您可以为任何方向的波动做好准备,一旦价格走势确认了突破方向,就会自动进入市场。


账户属性类

在MQL5中,CAccountProperties类继承自CAccountInfo类。该类的目的是通过添加一个自定义方法来扩展CAccountInfo类,用于检索交易账户中特定类型订单的总数,例如买入或卖出的限价单/止损单。这个额外的函数将在交易管理类中使用,以确保EA不会超出账户的订单限制。

例如:如果账户的订单限制是200,这意味着用户的账户不能超过200个订单,包括已开仓订单和挂单。因此,如果我们想开仓一个买入止损单和一个卖出止损单,但用户的账户已经有199个订单,那么EA会识别出没有足够的订单空间来同时开设这两个订单。在这种情况下,不会向用户的账户添加任何额外的订单。

头文件和包含项:

以下代码包含了MQL5标准库中的AccountInfo.mqh文件。该文件包含了预定义的函数和结构,用于访问交易账户信息。

#include <Trade/AccountInfo.mqh>

CAccountProperties类定义:

继承:CAccountProperties类公开继承自CAccountInfo类。通过继承,CAccountProperties类获得了访问CAccountInfo类的函数和数据成员的能力。

CAccountInfo类提供了基本的账户数据,如余额、净值、可用保证金等。

类的目的:CAccountProperties类添加了一个方法(numOrders),用于计算特定类型的订单数量。

class CAccountProperties: public CAccountInfo

numOrders函数:

numOrders()函数用于执行大部分操作。该函数计算当前在交易账户中已开仓的特定类型订单(限价单和止损单)的数量。

  • 返回类型:该函数返回一个整数型。
  • 初始化:变量num被初始化为0。这将存储与所需类型匹配的订单数量。

int CAccountProperties::numOrders(void)
{
   int num=0;

订单遍历:

  • OrdersTotal()是MQL5中的一个函数,用于返回订单的总数。
  • 使用一个循环来遍历所有订单,从索引0到OrdersTotal() - 1。

for(int i=0; i<OrdersTotal(); i++)

订单验证:

  • OrderGetTicket(i)是MQL5中的一个函数,用于检索索引i处订单的编号。每个订单都有一个唯一的订单编号。
  • 如果订单编号大于0,意味着订单是有效的,代码将继续检查其类型。

if(OrderGetTicket(i)>0)

检查订单类型:

  • OrderGetInteger(ORDER_TYPE)检索索引i的订单类型。该值对应于一个整数,代表各种订单类型(如买入限价单、卖出止损单等)。
  • switch语句检查每个订单的类型,并将其与预定义的订单类型常量进行比较。
switch(int(OrderGetInteger(ORDER_TYPE)))

处理特定订单类型:

  • 订单类型:
    • ORDER_TYPE_BUY_LIMIT:买入限价单。
    • ORDER_TYPE_BUY_STOP:买入止损单。
    • ORDER_TYPE_BUY_STOP_LIMIT:买入止损限价单。
    • ORDER_TYPE_SELL_LIMIT:卖出限价单。
    • ORDER_TYPE_SELL_STOP:卖出止损单。
    • ORDER_TYPE_SELL_STOP_LIMIT:卖出止损限价单。

对于每一个识别出的订单类型,计数器num都会递增。如果订单是上述之外的其他任何类型,则会触发默认情况,并且不采取任何行动。

case ORDER_TYPE_BUY_LIMIT:
    num++;
    break;
case ORDER_TYPE_BUY_STOP:
    num++;
    break;
case ORDER_TYPE_BUY_STOP_LIMIT:
    num++;
    break;
case ORDER_TYPE_SELL_LIMIT:
    num++;
    break;
case ORDER_TYPE_SELL_STOP:
    num++;
    break;
case ORDER_TYPE_SELL_STOP_LIMIT:
    num++;
    break;
default:
    break;

返回计数结果:

  • 在遍历完所有订单并统计了有效订单的数量后,函数返回存储在num变量中的总数。

   return num;
}



风险管理类

该类确保交易者对市场风险的暴露得到控制,在限制潜在损失的同时为盈利留出空间。需要说明的是,此类已经进行了一些小的更新。下面的代码定义了一个枚举类型OrderTypeSelection,用于表示不同类型的订单分类。允许用户选择自身更倾向于交易的订单类型。
//-- Enumeration for Order type
enum OrderTypeSelection
  {
   MarketPositionType,//MARKET POSITION
   StopOrdersType,//STOP ORDERS
   StopOrderType,//SINGLE STOP ORDER
  } myOrderSelection;

枚举:OrderTypeSelection

OrderTypeSelection枚举包含三种不同的值:

MarketPositionType:

  • 表示市场价订单。
  • 这类订单类型基于市场当前价格开仓,并立即以当前价格执行。
  • 受事件影响。

StopOrdersType:

  • 表示止损单。
  • 止损单是条件性订单,仅在价格达到某一特定水平时执行。可以同时开设买入止损单和卖出止损单。
  • 不受事件影响。

StopOrderType:

  • 表示单个止损单。
  • 仅开设一个买入止损单或卖出止损单。
  • 受事件影响。

变量声明:myOrderSelection

  • myOrderSelection是一个变量,用于存储交易者当前选择的订单类型。

交易量标准化

在下面的函数中,交易量限制会根据OrderTypeSelection的不同而变化。当myOrderSelection为 StopOrdersType(即止损单类型)时,交易量限制会减半,以适应同时开设买入止损单和卖出止损单的需求(因为两者需要同时开仓),而MarketPositionType(市场价订单类型)和StopOrderType(单个止损单类型)则只开设单个订单。

void CRiskManagement::NormalizeLotsize(double &Lotsize)
{
    // Adjust lot size to match symbol's step size or minimum size
    if (Lotsize <= 0.0) return;
    double VolumeLimit = (myOrderSelection != StopOrdersType) ? CSymbol.LotsLimit() : CSymbol.LotsLimit() / 2;
    // Check and adjust if volume exceeds limits
    // ...
}



时间区间类

以下代码定义了一个CSessions类,该类负责管理和跟踪特定交易品种的交易时间段。它使用从基类CTimeManagement(通过Timemanagement.mqh包含)中继承的方法来确定交易时段是否已经开始、是否已经结束,以及获取交易时段的结束时间。在实际应用中,我们需要知道交易时段何时结束,以便提前平仓并设置订单的到期日期,从而避免隔夜交易。

为什么要避免隔夜交易?

隔夜交易

因波动性增加而导致的市场风险

市场在隔夜时段可能会高度波动,尤其是在应对经济事件、地缘政治发展或其他全球市场新闻时。由于重大事件可能发生在正常交易时间之外,因此隔夜持仓会暴露于不可预测的价格波动之中。这些波动可能导致:

  • 次日市场开盘时出现显著的价格缺口,可能导致大幅盈利或亏损。
  • 应对快速变化的能力受限,因为隔夜时段的流动性可能较低,您快速修改或平仓的能力可能受到限制。

流动性受限

隔夜交易时段的流动性往往较低,因为活跃的市场参与者较少。这可能导致:

  • 买卖价差扩大,交易成本增加。
  • 滑点,即由于市场深度不足,交易以不同于预期的价格成交。
  • 难以在不引起市场波动的情况下平仓大额头寸,可能导致不利的成交价格。

缺口风险增加

隔夜交易可能使交易者面临价格缺口的风险,即市场开盘价与前一日收盘价显著不同。缺口可能因新闻发布或其他事件而发生,并可能导致:

  • 超出预期的亏损,尤其是当价格走势与您的头寸方向相反时。
  • 止损订单失效,因为缺口可能跳过您预先设定的止损水平,导致订单以远低于预期的价格成交。

利息费用(隔夜利息)

对于某些交易品种,如外汇,隔夜持仓可能导致隔夜利息费用或利息支出,这取决于所涉及货币之间的利率差异。这些成本会随时间累积并降低盈利能力。可能产生的隔夜利息费用:

  • 将原本盈利的交易转变为亏损交易,尤其是当持仓时间较长时。
  • 因市场条件而变化,使其更难预测和规划。

对交易执行的控制减少

在夜间,尤其是在交易时间有限或不规则的市场中,交易执行可能较慢或不够精确,原因包括:

  • 经纪商营业时间限制:一些经纪商可能在其正常营业时间之外不支持某些交易操作或修改,从而降低了您应对市场条件的能力。

//+------------------------------------------------------------------+
//|                                                      NewsTrading |
//|                                  Copyright 2024, MetaQuotes Ltd. |
//|                            https://www.mql5.com/en/users/kaaiblo |
//+------------------------------------------------------------------+
#include "Timemanagement.mqh"
//+------------------------------------------------------------------+
//|Sessions Class                                                    |
//+------------------------------------------------------------------+
class CSessions:CTimeManagement
  {
public:
                     CSessions(void) {}
                    ~CSessions(void) {}
   //--- Check if trading Session has began
   bool              isSessionStart(int offsethour=0,int offsetmin=0);
   //--- Check if trading Session has ended
   bool              isSessionEnd(int offsethour=0,int offsetmin=45);
   //--- Get Session End datetime
   datetime          SessionEnd(int offsethour=0,int offsetmin=45);
  };
//+------------------------------------------------------------------+
//|Check if trading Session has started                              |
//+------------------------------------------------------------------+
bool CSessions::isSessionStart(int offsethour=0,int offsetmin=0)
  {
//--- Declarations
   datetime datefrom,dateto,DateFrom[],DateTo[];

//--- Find all session times
   for(int i=0; i<10; i++)
     {
      //--- Get the session dates for the current symbol and Day of week
      if(SymbolInfoSessionTrade(Symbol(),DayOfWeek(TimeTradeServer()),i,datefrom,dateto))
        {
         //--- Check if the end date's hour is at midnight
         if(ReturnHour(dateto)==00||ReturnHour(dateto)==24)
           {
            //--- Adjust the date to one minute before midnight
            dateto = Time(TimeTradeServer(),23,59);
           }
         //--- Re-adjust DateFrom Array size
         ArrayResize(DateFrom,int(ArraySize(DateFrom))+1,int(ArraySize(DateFrom))+2);
         //--- Assign the last array index datefrom value
         DateFrom[int(ArraySize(DateFrom))-1] = datefrom;
         //--- Re-adjust DateTo Array size
         ArrayResize(DateTo,int(ArraySize(DateTo))+1,int(ArraySize(DateTo))+2);
         //--- Assign the last array index dateto value
         DateTo[int(ArraySize(DateTo))-1] = dateto;
        }
     }

//--- Check if there are session times
   if(DateFrom.Size()>0)
     {
      /* Adjust DateFrom index zero date as the first index date will be the earliest date
       from the whole array, we add the offset to this date only*/
      DateFrom[0] = TimePlusOffset(DateFrom[0],MinutesS(offsetmin));
      DateFrom[0] = TimePlusOffset(DateFrom[0],HoursS(offsethour));
      //--- Iterate through the whole array
      for(uint i=0; i<DateFrom.Size(); i++)
        {
         //--- Check if the current time is within the trading session
         if(TimeIsInRange(Time(Today(ReturnHour(DateFrom[i]),ReturnMinute(DateFrom[i])))
                          ,Time(Today(ReturnHour(DateTo[i]),ReturnMinute(DateTo[i])))))
           {
            return true;
           }
        }
     }
   else
     {
      //--- If there are no trading session times
      return true;
     }
   return false;
  }

//+------------------------------------------------------------------+
//|Check if trading Session has ended                                |
//+------------------------------------------------------------------+
bool CSessions::isSessionEnd(int offsethour=0,int offsetmin=45)
  {
//--- Declarations
   datetime datefrom,dateto,DateTo[],lastdate=0,sessionend;

//--- Find all session times
   for(int i=0; i<10; i++)
     {
      //--- Get the session dates for the current symbol and Day of week
      if(SymbolInfoSessionTrade(Symbol(),DayOfWeek(TimeTradeServer()),i,datefrom,dateto))
        {
         //--- Check if the end date's hour is at midnight
         if(ReturnHour(dateto)==00||ReturnHour(dateto)==24)
           {
            //--- Adjust the date to one minute before midnight
            dateto = Time(TimeTradeServer(),23,59);
           }
         //--- Re-adjust DateTo Array size
         ArrayResize(DateTo,int(ArraySize(DateTo))+1,int(ArraySize(DateTo))+2);
         //--- Assign the last array index dateto value
         DateTo[int(ArraySize(DateTo))-1] = dateto;
        }
     }

//--- Check if there are session times
   if(DateTo.Size()>0)
     {
      //--- Assign lastdate a default value
      lastdate = DateTo[0];
      //--- Iterate through the whole array
      for(uint i=0; i<DateTo.Size(); i++)
        {
         //--- Check for the latest date in the array
         if(DateTo[i]>lastdate)
           {
            lastdate = DateTo[i];
           }
        }
     }
   else
     {
      //--- If there are no trading session times
      return false;
     }
   /* get the current time and modify the hour and minute time to the lastdate variable
   and assign the new datetime to sessionend variable*/
   sessionend =  Time(Today(ReturnHour(lastdate),ReturnMinute(lastdate)));
//--- Re-adjust the sessionend dates with the minute and hour offsets
   sessionend = TimeMinusOffset(sessionend,MinutesS(offsetmin));
   sessionend = TimeMinusOffset(sessionend,HoursS(offsethour));

//--- Check if sessionend date is more than the current time
   if(TimeTradeServer()<sessionend)
     {
      return false;
     }
   return true;
  }

//+------------------------------------------------------------------+
//|Get Session End datetime                                          |
//+------------------------------------------------------------------+
datetime CSessions::SessionEnd(int offsethour=0,int offsetmin=45)
  {
//--- Declarations
   datetime datefrom,dateto,DateTo[],lastdate=0,sessionend;

//--- Find all session times
   for(int i=0; i<10; i++)
     {
      //--- Get the session dates for the current symbol and Day of week
      if(SymbolInfoSessionTrade(Symbol(),DayOfWeek(TimeTradeServer()),i,datefrom,dateto))
        {
         //--- Check if the end date's hour is at midnight
         if(ReturnHour(dateto)==00||ReturnHour(dateto)==24)
           {
            //--- Adjust the date to one minute before midnight
            dateto = Time(TimeTradeServer(),23,59);
           }
         //--- Re-adjust DateTo Array size
         ArrayResize(DateTo,int(ArraySize(DateTo))+1,int(ArraySize(DateTo))+2);
         //--- Assign the last array index dateto value
         DateTo[int(ArraySize(DateTo))-1] = dateto;
        }
     }

//--- Check if there are session times
   if(DateTo.Size()>0)
     {
      //--- Assign lastdate a default value
      lastdate = DateTo[0];
      //--- Iterate through the whole array
      for(uint i=0; i<DateTo.Size(); i++)
        {
         //--- Check for the latest date in the array
         if(DateTo[i]>lastdate)
           {
            lastdate = DateTo[i];
           }
        }
     }
   else
     {
      //--- If there are no trading session times
      return 0;
     }
   /* get the current time and modify the hour and minute time to the lastdate variable
   and assign the new datetime to sessionend variable*/
   sessionend = Time(Today(ReturnHour(lastdate),ReturnMinute(lastdate)));
//--- Re-adjust the sessionend dates with the minute and hour offsets
   sessionend = TimeMinusOffset(sessionend,MinutesS(offsetmin));
   sessionend = TimeMinusOffset(sessionend,HoursS(offsethour));
//--- return sessionend date
   return sessionend;
  }
//+------------------------------------------------------------------+

关键组件解析

类定义:CSessions

  • 继承:
    • CSessions类继承自CTimeManagement,因此它可以访问基类中定义的任何与时间管理相关的方法和属性。
  • 方法:
    • isSessionStart(int offsethour=0, int offsetmin=0):
      • 通过将当前服务器时间与交易时段开始时间进行比较,确定交易时段是否已经开始。允许传入可选的时间偏移量(小时和分钟)。
    • isSessionEnd(int offsethour=0, int offsetmin=45):
      • 通过将当前服务器时间与交易时段结束时间进行比较,确定交易时段是否已经结束。与isSessionStart类似,接受可选的偏移量(默认45分钟)。
    • SessionEnd(int offsethour=0, int offsetmin=45):
      • 返回当前交易时段结束时间的datetime值,并根据提供的偏移量进行调整(默认45分钟)。

方法详情

isSessionStart(int offsethour=0, int offsetmin=0)

此方法根据服务器时间和交易品种的交易时段安排,检查交易时段是否已经开始。

变量:

  • datefrom, dateto:这两个变量用于存储每个交易日交易时段的开始和结束时间。
  • DateFrom[], DateTo[]:这些数组用于在调整后存储交易时段的开始和结束时间。

逻辑如下 :

  • 时段获取:
    • SymbolInfoSessionTrade函数用于获取当前交易品种在当前星期几(通过 DayOfWeek(TimeTradeServer()) 获取)交易时段的开始时间(datefrom)和结束时间(dateto)。
    • 如果时段结束时间(dateto)是午夜(00:00或24:00),则时间调整为午夜前一分钟(23:59)。
  • 数组操作:
    • 交易时段的开始时间(datefrom)存储在 DateFrom[] 数组中,结束时间(dateto)存储在 DateTo[] 数组中。
  • 偏移量应用:
    • 该方法使用辅助函数MinutesS() 和HoursS(),根据给定的offsethour(小时偏移量)和offsetmin(分钟偏移量)调整第一个交易时段的开始时间。
  • 检查当前时间:
    • 然后,该方法遍历DateFrom[] 和DateTo[] 数组中的交易时段时间,并检查当前服务器时间(TimeTradeServer())是否落在任何一个交易时段区间内。
    • 如果找到有效的交易时段,则返回true,表示交易时段已经开始。否则,返回false。

isSessionEnd(int offsethour=0, int offsetmin=45)

该方法用于检查交易时段是否已经结束。

变量:

  • 与isSessionStart()相类似,但侧重于结束时间(dateto)。
  • lastdate:存储用于比较的最新交易时段结束时间。
  • sessionend:保存应用偏移量后计算得出的最终交易时段结束时间。

逻辑如下 :

  • 时段获取:
    • 像isSessionStart()一样检索交易时段时间(datefrom、dateto)。
    • 检查DateTo[] 中是否存在有效的交易时段时间,并确定最新的交易时段结束时间(lastdate)。
  • 偏移量应用:
    • 通过应用偏移量(offsethour 和 offsetmin)调整交易时段结束时间。
  • 与服务器时间比较:
    • 如果当前时间早于调整后的交易时段结束时间,则返回false,表示交易时段仍在持续中。如果交易时段已结束(当前时间晚于 sessionend),则返回true。

SessionEnd(int offsethour=0, int offsetmin=45)

此方法返回当前交易时段结束时间的日期时间值,并应用提供的时间偏移量。

逻辑如下 :

  • 与isSessionEnd()相类似,其检索交易时段时间,应用偏移量,并返回最终调整后的交易时段结束时间(sessionend)。
隔夜交易的弊端总结:
  • 市场波动加剧,走势难以预测。
  • 流动性低,导致买卖价差扩大,成本增加。
  • 存在价格跳空风险,可能导致较大损失。
  • 易受全球事件及其市场影响。
  • 外汇市场中存在利息费用或掉期费用。
  • 对交易执行和决策的控制力减弱。


交易管理类

CTradeManagement类继承自CRiskManagement,包含管理交易的功能,例如执行买入/卖出订单、处理止损和止盈水平以及管理止损订单。

执行买入或卖出订单的流程图:

+---------------------------+
| Receive Trade Signal      |
+---------------------------+
              |
              V
+---------------------------+
| Check for Available Margin|
+---------------------------+
              |
              V
+---------------------------+
| Place Buy/Sell Order      |
+---------------------------+
              |
              V
+----------------------------+
| Set Stop Loss & Take Profit|
+----------------------------+
              |
              V
+----------------------------+
| Monitor Open Position      |
+----------------------------+
              |
        If conditions met:
              |
              V
+----------------------------+
| Adjust SL/TP or Close Trade|
+----------------------------+
//+------------------------------------------------------------------+
//|                                                      NewsTrading |
//|                                  Copyright 2024, MetaQuotes Ltd. |
//|                            https://www.mql5.com/en/users/kaaiblo |
//+------------------------------------------------------------------+
#include <Trade\Trade.mqh>
#include <Trade\OrderInfo.mqh>
#include <Trade\SymbolInfo.mqh>
#include "RiskManagement.mqh"
#include "TimeManagement.mqh"
#include "Sessions.mqh"

//+------------------------------------------------------------------+
//|TradeManagement class                                             |
//+------------------------------------------------------------------+
class CTradeManagement:CRiskManagement
  {
private:
   CTrade            Trade;//Trade class object
   CSymbolProperties CSymbol;//SymbolProperties class object
   CTimeManagement   CTime;//TimeManagement class object
   CSessions         CTS;//Sessions class object
   bool              TradeResult;//boolean to store trade result
   double            mySL;//double variable to store Stoploss
   double            myTP;//double variable to store Takeprofit
   uint              myDeviation;//store price deviation for stop orders
   double            myOpenPrice;//store open price for stop orders
   //--- Will retrieve if there are any open trades
   bool              OpenTrade(ENUM_POSITION_TYPE Type,ulong Magic,string COMMENT=NULL);
   //--- Will retrieve if there are any deals
   bool              OpenedDeal(ENUM_DEAL_TYPE Type,ulong Magic,string COMMENT=NULL);
   //--- Will retrieve if there are any open orders
   bool              OpenOrder(ENUM_ORDER_TYPE Type,ulong Magic,string COMMENT=NULL);
   //--  Check if trade is valid
   bool              Valid_Trade(ENUM_POSITION_TYPE Type,double Price,double SL,double TP);
   //-- Check if stop order is valid
   bool              Valid_Order(ENUM_ORDER_TYPE Type,double Price,double SL,double TP);
   //--- Will attempt to open buy trade
   bool              Buy(double SL,double TP,ulong Magic,string COMMENT=NULL);
   //--- Will attempt to open sell trade
   bool              Sell(double SL,double TP,ulong Magic,string COMMENT=NULL);

   //--- class to set and retrieve an order's properties
   class OrderSettings
     {
   private:
      struct TradeProperties
        {
         //store open-price,take-profit,stop-loss for stop orders
         double         Open,Take,Stop;
        } myTradeProp;
   public:
                     OrderSettings() {}
      //--- Set order properties
      void           Set(double myOpen,double myTake,double myStop)
        {
         //--- Set open-price
         myTradeProp.Open=myOpen;
         //--- Set take-profit
         myTradeProp.Take=myTake;
         //--- Set stop-loss
         myTradeProp.Stop=myStop;
        }
      TradeProperties Get()
        {
         //--- retrieve order properties
         return myTradeProp;
        }
     };

   //--- Declare variables for different order types
   OrderSettings     myBuyStop,mySellStop,myBuyTrade,mySellTrade;

   //--- Will set buy-stop order properties
   void              SetBuyStop(int SL,int TP);
   //--- Will set buy position properties
   void              SetBuyTrade(int SL,int TP,double OP);
   //--- Will set sell-stop order properties
   void              SetSellStop(int SL,int TP);
   //--- Will set sell position properties
   void              SetSellTrade(int SL,int TP,double OP);

public:
   //--- Class constructor
                     CTradeManagement(uint deviation,string SYMBOL=NULL)
                     :myDeviation(deviation)//Assign deviation value
     {
      //--- Set symbol name
      CSymbol.SetSymbolName(SYMBOL);
     }
   //--- Class destructor
                    ~CTradeManagement(void)
     {
     }
   //--- Will attempt to open buy trade
   bool              Buy(int SL,int TP,ulong Magic,string COMMENT=NULL);
   //--- Will attempt to open sell trade
   bool              Sell(int SL,int TP,ulong Magic,string COMMENT=NULL);
   /*This function will delete a pending order if the previous opposing pending order is
   opened into a position, this function is used when trading with StopOrdersType(STOP ORDERS)*/
   void              FundamentalMode(string COMMENT_COMMON);
   /* Function will attempt to re-adjust stop-losses or take-profit values that have
   been changed due to slippage on an order when opening.
   */
   void              SlippageReduction(int SL,int TP,string COMMENT_COMMON);
   //--- This function will open both buy-stop and sell-stop orders for StopOrdersType(STOP ORDERS)
   bool              OpenStops(int SL,int TP,ulong Magic,string COMMENT=NULL);
   //--- Will attempt to open a sell-stop order
   bool              OpenSellStop(int SL,int TP,ulong Magic,string COMMENT=NULL);
   //--- Will attempt to open a buy-stop order
   bool              OpenBuyStop(int SL,int TP,ulong Magic,string COMMENT=NULL);
   //--- Function will attempt to close all trades depending on the position comment
   void              CloseTrades(string COMMENT_COMMON);
  };
// ...

私有成员:

  • 对象和变量:
    • CTrade Trade:管理交易执行(买入/卖出订单)等操作。
    • CSymbolProperties CSymbol:管理交易品种属性。
    • CTimeManagement CTime:处理与时间相关的逻辑。
    • CSessions CTS:管理会话,这些会话与交易时间相关。
    • bool TradeResult:表示交易结果的布尔标识(成功/失败)。
    • double mySL:止损值。
    • double myTP:止盈值。
    • uint myDeviation:止损订单的价格偏差。
    • double myOpenPrice:存储止损订单的开仓价格。

OrderSettings内部类:

class OrderSettings
{
private:
   struct TradeProperties
   {
      double Open, Take, Stop;
   } myTradeProp;
public:
   OrderSettings() {}
   void Set(double myOpen, double myTake, double myStop)
   {
      myTradeProp.Open = myOpen;
      myTradeProp.Take = myTake;
      myTradeProp.Stop = myStop;
   }
   TradeProperties Get()
   {
      return myTradeProp;
   }
};

  • 目的:这个内部类封装了交易属性(开仓价格、止盈、止损)。它允许轻松地设置和获取这些属性。
    • myTradeProp:一个用于存储交易属性的结构体。
    • Set():用于设置开仓、止盈和止损值的方法。
    • Get():检索已存储的交易属性的方法。
  • 使用OrderSettings变量:

OrderSettings myBuyStop, mySellStop, myBuyTrade, mySellTrade;

    • 这些对象用于存储不同订单类型(买入止损、卖出止损、买入交易、卖出交易)的属性。

以下FundamentalMode函数旨在当相反方向的订单已被执行并开仓为持仓时,删除待处理的止损订单。这一功能对于同时使用买入止损和卖出止损订单(如跨式策略)在波动市场中开仓的策略尤为重要。一旦其中一个止损订单被触发并开仓,剩余的相反方向订单(此时已变得多余)将被删除,以避免不必要的交易。

例如:

在1.13118价位执行买入止损订单,在1.12911价位执行卖出止损订单,买入止损订单首先被执行,日志消息为“交易已执行 [#2 买入 0.01 欧元/美元于 1.13134]”。在这种情况下,函数将删除/取消剩余的卖出止损订单,日志消息为“订单已取消 [#3 卖出止损 0.01 欧元/美元于 1.12911]”,如下图所示。 

由于FundamentalMode而取消的订单

//+------------------------------------------------------------------+
//|This function will delete a pending order if the previous opposing|
//|pending order is opened into a position, this function is used    |
//|when trading with StopOrdersType(STOP ORDERS)                     |
//+------------------------------------------------------------------+
void CTradeManagement::FundamentalMode(string COMMENT_COMMON)
  {
//--- Iterate through all open positions if Orders are more than zero
   for(int P=0; P<PositionsTotal()&&OrdersTotal()>0; P++)
     {
      //--- Check if Position ticket is above zero
      if(PositionGetTicket(P)>0)
        {
         //--- Check if the Position's Symbol,Magic,Type,Comment is correct
         if(PositionGetString(POSITION_SYMBOL)==CSymbol.GetSymbolName()&&
            StringFind(PositionGetString(POSITION_COMMENT),COMMENT_COMMON)>=0)
           {
            //--- Iterate through all open orders
            for(int O=0; O<OrdersTotal(); O++)
              {
               //--- Check if Order ticket is above zero
               if(OrderGetTicket(O)>0)
                 {
                  //--- Check if the Order's Symbol,Magic,Comment is correct
                  if(OrderGetString(ORDER_SYMBOL)==CSymbol.GetSymbolName()
                     &&OrderGetInteger(ORDER_MAGIC)==PositionGetInteger(POSITION_MAGIC)
                     &&StringFind(OrderGetString(ORDER_COMMENT),COMMENT_COMMON)>=0
                     &&OrderGetString(ORDER_COMMENT)==PositionGetString(POSITION_COMMENT))
                    {
                     //--- Identify Position type
                     switch(int(PositionGetInteger(POSITION_TYPE)))
                       {
                        /* In the case that the Position type is a buy and if the corresponding order type is
                        a sell-stop then delete this order*/
                        case  POSITION_TYPE_BUY:
                           if(OrderGetInteger(ORDER_TYPE)==ORDER_TYPE_SELL_STOP)
                             {
                              //--- Delete the sell-stop order
                              Trade.OrderDelete(OrderGetTicket(O));
                             }
                           break;
                        /* In the case that the Position type is a sell and if the corresponding order type is
                        a buy-stop then delete this order*/
                        case POSITION_TYPE_SELL:
                           if(OrderGetInteger(ORDER_TYPE)==ORDER_TYPE_BUY_STOP)
                             {
                              //--- Delete the sell-stop order
                              Trade.OrderDelete(OrderGetTicket(O));
                             }
                           break;
                        default:
                           break;
                       }
                    }
                 }
              }
           }
        }
     }
  }

函数目的:

该函数确保当待处理订单(例如买入止损或卖出止损订单)被开仓交易时,相反方向的订单(例如卖出止损或买入止损订单)被删除。这样可以防止两个订单都被执行,从而避免开立不希望持有的仓位。

void CTradeManagement::FundamentalMode(string COMMENT_COMMON)

  • FundamentalMode:此函数尝试在相反方向的仓位已开立时删除待处理的止损订单。
  • COMMENT_COMMON:一个字符串参数,用于根据与交易/订单关联的注释来识别交易/订单。

遍历已开仓位:

for(int P = 0; P < PositionsTotal() && OrdersTotal() > 0; P++)

  • PositionsTotal():返回已开仓位的总数。
  • OrdersTotal():返回待处理订单的总数。
  • 此for循环遍历所有已开仓位,并确保有待处理订单需要处理。

仓位单号验证:

if(PositionGetTicket(P) > 0)

  • PositionGetTicket(P):检索索引为P的仓位单号。
  • 确保仓位单号有效(即大于0)。

检查仓位属性:

if(PositionGetString(POSITION_SYMBOL) == CSymbol.GetSymbolName() &&
   StringFind(PositionGetString(POSITION_COMMENT), COMMENT_COMMON) >= 0)

  • PositionGetString(POSITION_SYMBOL):检索已开仓位的交易品种。
  • CSymbol.GetSymbolName():检索构造函数中设置的交易品种名称。
  • PositionGetString(POSITION_COMMENT):检索与仓位关联的注释。
  • StringFind(PositionGetString(POSITION_COMMENT), COMMENT_COMMON):检查COMMENT_COMMON子字符串是否存在于仓位的注释中。它确保仓位属于该策略/EA。

遍历待处理订单:

for(int O = 0; O < OrdersTotal(); O++)

  • 遍历所有待处理的订单。

订单单号验证:

if(OrderGetTicket(O) > 0)

  • OrderGetTicket(O):检索索引为O的订单单号。
  • 确保订单单号有效。

检查订单属性:

if(OrderGetString(ORDER_SYMBOL) == CSymbol.GetSymbolName() &&
   OrderGetInteger(ORDER_MAGIC) == PositionGetInteger(POSITION_MAGIC) &&
   StringFind(OrderGetString(ORDER_COMMENT), COMMENT_COMMON) >= 0 &&
   OrderGetString(ORDER_COMMENT) == PositionGetString(POSITION_COMMENT))

  • OrderGetString(ORDER_SYMBOL):检索与订单关联的交易品种。
  • OrderGetInteger(ORDER_MAGIC):检索订单的magic数字(有助于识别每个事件对应的订单)。
  • PositionGetInteger(POSITION_MAGIC):检索仓位的magic数字,以便与订单的magic数字进行匹配。
  • OrderGetString(ORDER_COMMENT):检索订单的注释。
  • 确保订单的交易品种、magic数字和注释与仓位的属性相匹配。这样确保待处理订单与已开仓位相关联。

识别并删除相反方向的订单:

switch(int(PositionGetInteger(POSITION_TYPE)))
{
   case POSITION_TYPE_BUY:
      if(OrderGetInteger(ORDER_TYPE) == ORDER_TYPE_SELL_STOP)
      {
         Trade.OrderDelete(OrderGetTicket(O));
      }
      break;
   case POSITION_TYPE_SELL:
      if(OrderGetInteger(ORDER_TYPE) == ORDER_TYPE_BUY_STOP)
      {
         Trade.OrderDelete(OrderGetTicket(O));
      }
      break;
   default:
      break;
}

  • PositionGetInteger(POSITION_TYPE):检索已开仓位的类型(POSITION_TYPE_BUY表示买入仓位,POSITION_TYPE_SELL表示卖出仓位)。
  • OrderGetInteger(ORDER_TYPE):检索待处理订单的类型(ORDER_TYPE_BUY_STOP表示买入止损订单,ORDER_TYPE_SELL_STOP表示卖出止损订单)。

Switch Case:

  • 如果仓位是买入仓位(POSITION_TYPE_BUY),函数会检查是否存在待处理的卖出止损订单(ORDER_TYPE_SELL_STOP)。如果找到,则删除该卖出止损订单,因为已经不再需要。
  • 类似地,如果仓位是卖出仓位(POSITION_TYPE_SELL),函数会检查是否存在待处理的买入止损订单(ORDER_TYPE_BUY_STOP)。如果找到,则删除该买入止损订单。

Trade.OrderDelete(OrderGetTicket(O)):此方法使用订单的单号删除待处理订单,从而在相应仓位已开立后,有效移除不必要的相反方向订单。

SlippageReduction函数旨在确保已开仓位的止损(SL)和止盈(TP)水平被正确设置为预期值。如果在订单执行时,滑点(订单执行价格与预期价格之间的差异)导致SL和TP偏离了预期值,该函数会调整仓位以反映预期的SL和TP。

示例:交易者希望开立买入止损和卖出止损订单以交易NFP。交易者希望两个订单的价格偏差都为当前价格的100点,并且希望风险回报比为1:6,因此设置止损为100点,止盈为600点。在此情况下,参考下图,买入止损订单设置在1.13118,SL设置为1.13018,TP设置为1.13718,以保持1:6的投资回报率(ROI)。由于NFP事件的波动性,买入交易以不利的1.13134价格执行,日志消息为“订单已执行买入0.01于1.13134 [#2 买入止损0.01欧元兑美元于 1.13118]”。

买入止损订单的滑点情况:

将开仓价格的滑点计算为 - [实际价格 - 预期价格] / 点值

预期价格:1.13118|实际价格:1.13134|滑点:(1.13134 - 1.13118) / 0.00001 -> 16点(价格差异)

因此,为了保持投资回报率和止损水平,SL和TP值将相应调整16点。

应用滑点减少功能

//+------------------------------------------------------------------+
//|Function will attempt to re-adjust stop-losses or take-profit     |
//|values that have been changed due to slippage on an order when    |
//|opening.                                                          |
//+------------------------------------------------------------------+
void CTradeManagement::SlippageReduction(int SL,int TP,string COMMENT_COMMON)
  {
//--- Iterate through all open positions
   for(int i=0; i<PositionsTotal(); i++)
     {
      ulong ticket = PositionGetTicket(i);
      //--- Check if Position ticket is above zero
      if(ticket>0)
        {
         //--- Check if the Position's Symbol,Comment is correct
         if(PositionGetString(POSITION_SYMBOL)==CSymbol.GetSymbolName()
            &&StringFind(PositionGetString(POSITION_COMMENT),COMMENT_COMMON)>=0)
           {
            //--- Identify Position type
            switch(int(PositionGetInteger(POSITION_TYPE)))
              {
               case  POSITION_TYPE_BUY:
                  //--- set expect buy trade properties
                  SetBuyTrade(SL,TP,PositionGetDouble(POSITION_PRICE_OPEN));
                  //--- assign sl price
                  mySL = PositionGetDouble(POSITION_SL);
                  //--- assign tp price
                  myTP = PositionGetDouble(POSITION_TP);
                  //--- Normalize sl price
                  CSymbol.NormalizePrice(mySL);
                  mySL = double(DoubleToString(mySL,CSymbol.Digits()));
                  //--- Normalize tp price
                  CSymbol.NormalizePrice(myTP);
                  myTP = double(DoubleToString(myTP,CSymbol.Digits()));
                  //--- check if expected properties match actual trade properties
                  if((myBuyTrade.Get().Stop!=mySL||
                      myBuyTrade.Get().Take!=myTP)
                     &&Valid_Trade(POSITION_TYPE_BUY,myBuyTrade.Get().Open,
                                   myBuyTrade.Get().Stop,myBuyTrade.Get().Take))
                    {
                     //--- Modify position to respect expected properties
                     Trade.PositionModify(ticket,myBuyTrade.Get().Stop,myBuyTrade.Get().Take);
                    }
                  break;
               case POSITION_TYPE_SELL:
                  //--- set expect sell trade properties
                  SetSellTrade(SL,TP,PositionGetDouble(POSITION_PRICE_OPEN));
                  //--- assign sl price
                  mySL = PositionGetDouble(POSITION_SL);
                  //--- assign tp price
                  myTP = PositionGetDouble(POSITION_TP);
                  //--- Normalize sl price
                  CSymbol.NormalizePrice(mySL);
                  mySL = double(DoubleToString(mySL,CSymbol.Digits()));
                  //--- Normalize tp price
                  CSymbol.NormalizePrice(myTP);
                  myTP = double(DoubleToString(myTP,CSymbol.Digits()));
                  //--- check if expected properties match actual trade properties
                  if((mySellTrade.Get().Stop!=mySL||
                      mySellTrade.Get().Take!=myTP)
                     &&Valid_Trade(POSITION_TYPE_SELL,mySellTrade.Get().Open,
                                   mySellTrade.Get().Stop,mySellTrade.Get().Take))
                    {
                     //--- Modify position to respect expected properties
                     Trade.PositionModify(ticket,mySellTrade.Get().Stop,mySellTrade.Get().Take);
                    }
                  break;
               default:
                  break;
              }
           }
        }
     }
  }

函数目的:

该函数的目标是:

  1. 检查每个已开仓位。
  2. 验证仓位的止损(SL)和止盈(TP)值是否与预期值相匹配。
  3. 如果由于滑点导致它们不匹配,则修改仓位以反映正确的SL和TP值。

void CTradeManagement::SlippageReduction(int SL, int TP, string COMMENT_COMMON)

  • SlippageReduction:函数名称反映了其目的——如果在订单执行过程中由于滑点影响了SL和TP值,则对其进行调整。

参数:

  • SL:预期的止损值。
  • TP:预期的止盈值。
  • COMMENT_COMMON:用于过滤和识别属于该EA交易的字符串。

遍历已开仓位:

for (int i = 0; i < PositionsTotal(); i++)
{
    ulong ticket = PositionGetTicket(i);

  • PositionsTotal():此函数返回已开仓位的数量。
  • PositionGetTicket(i):检索索引为i的仓位单号。仓位单号唯一标识一个已开仓位。

仓位单号验证:

if (ticket > 0)

  • 确保仓位具有有效的单号,然后再继续执行后续步骤。

检查仓位属性:

if (PositionGetString(POSITION_SYMBOL) == CSymbol.GetSymbolName() &&
    StringFind(PositionGetString(POSITION_COMMENT), COMMENT_COMMON) >= 0)

  • PositionGetString(POSITION_SYMBOL):检索与已开仓位关联的交易品种。
  • CSymbol.GetSymbolName():获取交易策略的交易品种名称(例如,"EURUSD")。
  • PositionGetString(POSITION_COMMENT):检索附加到已开仓位的注释。
  • StringFind():检查COMMENT_COMMON是否是仓位注释的一部分,以确保函数仅处理与当前策略相关的仓位。

识别并处理仓位类型(买入或卖出):

情况1:买入仓位

case POSITION_TYPE_BUY:

  • 函数处理类型为BUY的仓位。

设置预期的买入交易属性:

SetBuyTrade(SL, TP, PositionGetDouble(POSITION_PRICE_OPEN));

  • SetBuyTrade(SL, TP, PositionGetDouble(POSITION_PRICE_OPEN)):为买入交易设置预期的止损、止盈和开仓价格。SetBuyTrade函数将这些预期值赋给myBuyTrade对象。

分配实际的SL和TP值:

mySL = PositionGetDouble(POSITION_SL);
myTP = PositionGetDouble(POSITION_TP);

  • PositionGetDouble(POSITION_SL):从已开仓位中检索实际的止损(SL)值。
  • PositionGetDouble(POSITION_TP):从已开仓位中检索实际的止盈(TP)值。

标准化SL和TP值:

CSymbol.NormalizePrice(mySL);
mySL = double(DoubleToString(mySL, CSymbol.Digits()));

CSymbol.NormalizePrice(myTP);
myTP = double(DoubleToString(myTP, CSymbol.Digits()));

  • NormalizePrice(mySL):将止损值标准化为正确的小数位数(基于交易品种的属性,例如,货币对可能有4或5位小数)。
  • DoubleToString(mySL, CSymbol.Digits()):将标准化后的SL值转换为字符串,然后再转换回双精度浮点数,以确保精度。
  • 同样的过程也应用于止盈(myTP)值。

比较预期和实际的SL/TP:

if ((myBuyTrade.Get().Stop != mySL || myBuyTrade.Get().Take != myTP) &&
    Valid_Trade(POSITION_TYPE_BUY, myBuyTrade.Get().Open, myBuyTrade.Get().Stop, myBuyTrade.Get().Take))
{
    Trade.PositionModify(ticket, myBuyTrade.Get().Stop, myBuyTrade.Get().Take);
}

  • myBuyTrade.Get().Stop:检索预期的止损值。
  • mySL:从仓位中检索到的实际止损值。
  • 如果实际的SL和TP值与预期值不匹配,那么函数会调用Trade.PositionModify来更新仓位,并设置正确的止损和止盈。
  • Valid_Trade():在尝试修改仓位之前,检查交易参数(价格、SL、TP)是否有效。

修改持仓:

Trade.PositionModify(ticket, myBuyTrade.Get().Stop, myBuyTrade.Get().Take);

  • 此函数根据预期值修改与仓位单号关联的仓位,以反映正确的止损和止盈值。

情况2:卖出仓位

  • 此过程与买入仓位的过程类似。

Valid_Trade函数根据交易类型(买入或卖出)以及与当前交易品种属性相关的止损和止盈规则,检查交易的参数(如价格、止损和止盈水平)是否有效。如果交易有效,该函数返回true;反之如果无效,则返回false。

//+------------------------------------------------------------------+
//|Check if a trade is valid                                         |
//+------------------------------------------------------------------+
bool CTradeManagement::Valid_Trade(ENUM_POSITION_TYPE Type,double Price,double SL,double TP)
  {
//--- Identify Position type
   switch(Type)
     {
      case  POSITION_TYPE_BUY:
         if((Price<TP||TP==0)&&(Price>SL||SL==0)&&
            ((int((Price-SL)/CSymbol.Point())>=CSymbol.StopLevel())
             ||(SL==0))&&
            ((int((TP-Price)/CSymbol.Point())>=CSymbol.StopLevel())
             ||(TP==0))&&
            Price>0
           )
           {
            //--- Trade properties are valid.
            return true;
           }
         break;
      case POSITION_TYPE_SELL:
         if((Price>TP||TP==0)&&(Price<SL||SL==0)&&
            ((int((Price-TP)/CSymbol.Point())>=CSymbol.StopLevel())
             ||(TP==0))&&
            ((int((SL-Price)/CSymbol.Point())>=CSymbol.StopLevel())
             ||(SL==0))&&
            Price>0
           )
           {
            //--- Trade properties are valid.
            return true;
           }
         break;
      default://Unknown
         return false;
         break;
     }
//--- Trade properties are not valid.
   Print("Something went wrong, SL/TP/Open-Price is incorrect!");
   return false;
  }

函数目的:

该函数的目标是:

  1. 验证买入或卖出仓位的交易参数(价格、止损 SL 和止盈 TP)。
  2. 确保止损和止盈水平相对于开仓价格设置正确,并且满足交易品种的止损水平要求。

参数:

bool CTradeManagement::Valid_Trade(ENUM_POSITION_TYPE Type, double Price, double SL, double TP)

  • Type:仓位类型,可以是POSITION_TYPE_BUY(买入仓位)或POSITION_TYPE_SELL(卖出仓位)。
  • Price:交易的开仓价格。
  • SL:交易的止损水平。
  • TP:交易的止盈水平。

switch语句:

该函数使用switch语句来处理不同类型的仓位(买入或卖出),并对每种情况应用不同的验证逻辑。

情况1:买入仓位

case POSITION_TYPE_BUY:

根据买入仓位的逻辑检查以下条件:

止盈(TP)验证:

if ((Price < TP || TP == 0)

  • 止盈水平必须大于开仓价格(Price < TP),或者可以设置为0(无止盈)。

止损(SL)验证:

(Price > SL || SL == 0)

  • 止损水平必须小于开仓价格(Price > SL),或者可以设置为0(无止损)。

交易品种的止损水平验证:

  • 交易品种的止损水平定义了价格与止损/止盈之间的最小距离。
  • 止损和止盈水平必须与开仓价格保持至少等于止损水平的最小差距。

止损差距验证:

((int((Price - SL) / CSymbol.Point()) >= CSymbol.StopLevel()) || SL == 0)

  • 价格与止损之间的差值必须至少等于交易品种的止损水平(以点为单位)。如果止损设置为0,则忽略此条件。

止盈差距验证:

((int((TP - Price) / CSymbol.Point()) >= CSymbol.StopLevel()) || TP == 0)

  • 止盈与价格之间的差值也必须满足止损水平要求。

最终验证:

&& Price > 0

  • 价格必须大于0。

如果所有这些条件都满足,则认为交易有效,函数返回true。

情况2:卖出仓位

对于卖出仓位,逻辑类似但方向相反。

默认情况:

default:
    return false;

如果仓位类型既不是POSITION_TYPE_BUY也不是POSITION_TYPE_SELL,则函数返回false,表示交易无效。

交易属性无效:

如果任何验证检查失败,函数会记录错误消息并返回false。

Print("Something went wrong, SL/TP/Open-Price is incorrect!");
return false;

此消息表示止损、止盈或开仓价格不正确。

以下代码定义了一个方法Valid_Order,该方法通过验证止损(SL)、止盈(TP)、价格和订单偏差的条件来检查止损订单(无论是BUY STOP还是SELL STOP订单)是否有效。如果订单有效,函数返回true;否则返回false。

//+------------------------------------------------------------------+
//|Check if stop order is valid                                      |
//+------------------------------------------------------------------+
bool CTradeManagement::Valid_Order(ENUM_ORDER_TYPE Type,double Price,double SL,double TP)
  {
//--- Identify Order type
   switch(Type)
     {
      case  ORDER_TYPE_BUY_STOP:
         if((Price<TP||TP==0)&&(Price>SL||SL==0)&&(Price>CSymbol.Ask())&&
            ((int((Price-SL)/CSymbol.Point())>=CSymbol.StopLevel())
             ||(SL==0))&&
            ((int((TP-Price)/CSymbol.Point())>=CSymbol.StopLevel())
             ||(TP==0))&&
            myDeviation>=uint(CSymbol.StopLevel())
           )
           {
            //--- Order properties are valid.
            return true;
           }
         break;
      case ORDER_TYPE_SELL_STOP:
         if((Price>TP||TP==0)&&(Price<SL||SL==0)&&(Price<CSymbol.Bid())&&
            ((int((Price-TP)/CSymbol.Point())>=CSymbol.StopLevel())
             ||(TP==0))&&
            ((int((SL-Price)/CSymbol.Point())>=CSymbol.StopLevel())
             ||(SL==0))&&
            myDeviation>=uint(CSymbol.StopLevel())
           )
           {
            //--- Order properties are valid.
            return true;
           }
         break;
      default://Other
         return false;
         break;
     }
//--- Order properties are not valid.
   Print("Something went wrong, SL/TP/Deviation/Open-Price is incorrect!");
   return false;
  }

概览

该函数根据止损订单的类型执行验证:

  1. BUY STOP(买入止损单):买入止损单设置在当前市场价格(卖出价)之上,当市场价格达到或超过此水平时触发。
  2. SELL STOP(卖出止损单):卖出止损单设置在当前市场价格(买入价)之下,当市场价格下跌至或低于此水平时触发。

参数:

bool CTradeManagement::Valid_Order(ENUM_ORDER_TYPE Type, double Price, double SL, double TP)

  • Type:止损订单的类型(可以是ORDER_TYPE_BUY_STOP或ORDER_TYPE_SELL_STOP)。
  • Price:止损订单设置的价格。
  • SL:与止损订单关联的止损价格。
  • TP:与止损订单关联的止盈价格。

switch语句:

switch语句处理不同的订单类型,并为每种类型应用不同的验证规则。

情况1:BUY STOP订单

case ORDER_TYPE_BUY_STOP:

对于BUY STOP订单,必须满足以下条件:

止盈(TP)验证:

(Price < TP || TP == 0)

止盈价格必须大于订单价格(Price < TP),或者可以设置为0(无止盈)。

止损(SL)验证:

(Price > SL || SL == 0)

止损价格必须小于订单价格(Price > SL),或者可以设置为0(无止损)。

价格高于卖出价:

(Price > CSymbol.Ask())

订单价格必须大于当前卖出价,这是BUY STOP订单的关键条件。

交易品种的止损水平验证:止损差距验证:

((int((Price - SL) / CSymbol.Point()) >= CSymbol.StopLevel()) || SL == 0)

订单价格与止损之间的差值必须满足或超过交易品种的止损水平,即订单价格与止损水平之间的最小允许差距(以点为单位)。如果止损设置为0,则跳过此条件。

止盈差距验证:

((int((TP - Price) / CSymbol.Point()) >= CSymbol.StopLevel()) || TP == 0)

止盈与订单价格之间的差值也必须满足止损水平要求,或者止盈可以设置为0。

价格偏差验证:

myDeviation >= uint(CSymbol.StopLevel())

价格偏差(滑点容忍度)必须大于或等于交易品种的止损水平。偏差允许订单在市场价格略微超出设定的止损水平时仍能执行。

如果所有这些条件都满足,则认为BUY STOP订单有效,函数返回true。

情况2:SELL STOP订单

对于SELL STOP订单,验证逻辑与BUY STOP订单相反。

默认情况:

default:
    return false;

如果订单类型既不是ORDER_TYPE_BUY_STOP也不是ORDER_TYPE_SELL_STOP,则函数返回false,表示订单无效。

错误处理:

如果条件不满足且函数无法验证订单,则会记录错误消息:

Print("Something went wrong, SL/TP/Deviation/Open-Price is incorrect!");

此消息通知用户止损、止盈、价格偏差或订单价格存在问题,导致订单无效。

下面的代码定义了一个函数SetBuyStop,负责设置买入止损订单的属性。它根据给定的输入计算止损(SL)和止盈(TP)价格,并将它们分配给订单。该函数执行以下步骤:

  1. 计算买入止损订单的开仓价格。
  2. 相对于开仓价格计算并设置止损和止盈价格。
  3. 对价格进行标准化处理,以确保它们与交易品种的精度(小数位数)对齐。

//+------------------------------------------------------------------+
//|Will set buy-stop order properties                                |
//+------------------------------------------------------------------+
void CTradeManagement::SetBuyStop(int SL,int TP)
  {
//-- Get Open-price
   myOpenPrice=CSymbol.Ask()+myDeviation*CSymbol.Point();
   CSymbol.NormalizePrice(myOpenPrice);
   NormalizeDouble(myOpenPrice,CSymbol.Digits());
//--- Get SL value
   mySL=SL*CSymbol.Point();
   mySL=myOpenPrice-mySL;
//--- Normalize the SL Price
   CSymbol.NormalizePrice(mySL);
   NormalizeDouble(mySL,CSymbol.Digits());
//--- Get TP value
   myTP=TP*CSymbol.Point();
   myTP+=myOpenPrice;
//--- Normalize the TP Price
   CSymbol.NormalizePrice(myTP);
   NormalizeDouble(myTP,CSymbol.Digits());
//--- Set BuyStop properties
   myBuyStop.Set(myOpenPrice,myTP,mySL);
  }

参数:

  • SL:以点数表示的止损值。
  • TP:以点数表示的止盈值。

void CTradeManagement::SetBuyStop(int SL, int TP)

SL和TP均为整数值,表示从开仓价格到止损或止盈水平的点数(价格增量)。

步骤1:计算买入止损订单的开仓价格

myOpenPrice = CSymbol.Ask() + myDeviation * CSymbol.Point();

  • CSymbol.Ask() 获取交易品种(货币对或资产)的当前卖出价。
  • myDeviation * CSymbol.Point() 以点数形式添加一个偏差(价格缓冲),以确保开仓价格高于当前卖出价。CSymbol.Point() 返回交易品种的一个点(最小价格变化)的大小。

标准化处理:

CSymbol.NormalizePrice(myOpenPrice);
NormalizeDouble(myOpenPrice, CSymbol.Digits());

  • CSymbol.NormalizePrice(myOpenPrice) 调整价格,使其与交易品种的精度对齐(即调整为有效的价格形式)。
  • NormalizeDouble(myOpenPrice, CSymbol.Digits()) 确保价格按照交易品种精度(CSymbol.Digits())定义的正确小数位数进行四舍五入。

步骤2:计算并标准化止损(SL)价格

mySL = SL * CSymbol.Point();
mySL = myOpenPrice - mySL;

  • SL * CSymbol.Point() 将整数止损值(以点数表示)转换为价格差值。
  • myOpenPrice - mySL通过从开仓价格中减去止损值(以点数表示)来计算止损价格。这是因为买入止损订单的止损设置在开仓价格之下。

标准化处理:

CSymbol.NormalizePrice(mySL);
NormalizeDouble(mySL, CSymbol.Digits());

  • 如上所述,止损价格经过标准化并四舍五入到正确的精度。

步骤3:计算并标准化止盈(TP)价格

myTP = TP * CSymbol.Point();
myTP += myOpenPrice;

  • TP * CSymbol.Point() 将整数止盈值(以点数表示)转换为价格差值。
  • myTP += myOpenPrice 将此值加到开仓价格上,因为买入止损订单的止盈设置在开仓价格之上。

标准化处理:

CSymbol.NormalizePrice(myTP);
NormalizeDouble(myTP, CSymbol.Digits());

  • 止盈价格经过标准化并四舍五入,与止损和开仓价格的处理方式类似。

步骤4:将计算出的值分配给买入止损订单

myBuyStop.Set(myOpenPrice, myTP, mySL);

  • myBuyStop.Set(myOpenPrice, myTP, mySL) 将计算出的开仓价格、止盈和止损值分配给myBuyStop订单对象。
  • 现在,myBuyStop对象将包含在市场中放置买入止损订单所需的属性。

SetBuyTrade函数用于设置买入仓位的属性。它计算并分配交易的正确开仓价格、止损(SL)和止盈(TP)。此函数用于配置买入仓位的参数(不是像买入止损这样的挂单)。买入仓位以当前市场价格开仓,并相对于开仓价格设置特定的止损和止盈水平。

参数:

  • SL:以点数(价格增量)表示的止损值。
  • TP:以点数表示的止盈值。
  • OP:交易的开仓价格(即仓位建立时的价格)。

步骤1:设置开仓价格

myOpenPrice = OP;
CSymbol.NormalizePrice(myOpenPrice);
myOpenPrice = double(DoubleToString(myOpenPrice, CSymbol.Digits()));

  • myOpenPrice = OP:函数将提供的开仓价格(OP)赋值给myOpenPrice。
  • CSymbol.NormalizePrice(myOpenPrice):根据交易品种的精度对开仓价格进行标准化处理,确保其符合正确的小数位数。
  • myOpenPrice = double(DoubleToString(myOpenPrice, CSymbol.Digits())):将价格进一步转换为字符串,然后再转换回具有交易品种正确小数位数(精度)的双精度浮点数。这样确保了价格数值处理的一致性,避免了浮点精度问题。

步骤2:计算并标准化止损(SL)价格

mySL = SL * CSymbol.Point();
mySL = myOpenPrice - mySL;

  • SL * CSymbol.Point():将止损值从点数转换为实际的价格差值。CSymbol.Point() 返回一个点的大小(交易品种的最小可能价格增量)。
  • myOpenPrice - mySL:通过从开仓价格中减去计算出的止损值来设置止损价格。在买入仓位中,止损设置在开仓价格之下,以防止交易遭受过大损失。

标准化处理:

CSymbol.NormalizePrice(mySL);
mySL = double(DoubleToString(mySL, CSymbol.Digits()));

  • 对止损价格进行标准化处理,以确保其具有正确的精度,并将其四舍五入到适当的小数位数。

步骤3:计算并标准化止盈(TP)价格

myTP = TP * CSymbol.Point();
myTP += myOpenPrice;

  • TP * CSymbol.Point():将止盈值从点数转换为价格差值。
  • myTP += myOpenPrice:将止盈价格加到开仓价格上,因为在买入仓位中,止盈设置在开仓价格之上。

标准化处理:

CSymbol.NormalizePrice(myTP);
myTP = double(DoubleToString(myTP, CSymbol.Digits()));

  • 对止盈价格进行标准化和四舍五入,以匹配交易品种的精度。

步骤4:将计算值分配给买入仓位

myBuyTrade.Set(myOpenPrice, myTP, mySL);

  • myBuyTrade.Set(myOpenPrice, myTP, mySL):将计算出的开仓价格、止损和止盈值分配给myBuyTrade对象。该对象现在包含将要建立或修改的买入仓位的所有相关属性。

OpenBuyStop函数尝试以指定的止损(SL)、止盈(TP)、magic数字和可选注释来建立买入止损订单。

参数:

  • SL:以点数表示的止损值。
  • TP:以点数表示的止盈值。
  • Magic:用于识别订单的唯一magic数字,通常用于EA。
  • COMMENT:(可选)附加到订单上的字符串注释,用于识别。

//+------------------------------------------------------------------+
//|Will attempt to open a buy-stop order                             |
//+------------------------------------------------------------------+
bool CTradeManagement::OpenBuyStop(int SL,int TP,ulong Magic,string COMMENT=NULL)
  {
   SetBuyStop(SL,TP);
//--- Set the order type for Risk management calculation
   SetOrderType(ORDER_TYPE_BUY);
//--- Set open price for Risk management calculation
   OpenPrice = myBuyStop.Get().Open;
//--- Set close price for Risk management calculation
   ClosePrice = myBuyStop.Get().Stop;
//--- Set Trade magic number
   Trade.SetExpertMagicNumber(Magic);
//--- Check if there are any open trades or opened deals or canceled deals already
   if(!OpenOrder(ORDER_TYPE_BUY_STOP,Magic,COMMENT)&&!OpenedDeal(DEAL_TYPE_BUY,Magic,COMMENT)
//--- Check if the buy-stop properties are valid
      &&Valid_Order(ORDER_TYPE_BUY_STOP,myBuyStop.Get().Open,myBuyStop.Get().Stop,myBuyStop.Get().Take))
     {
      //--- Iterate through the Lot-sizes if they're more than max-lot
      for(double i=Volume();i>=CSymbol.LotsMin()&&
          /* Check if current number of orders +1 more orders is less than
                 account orders limit.*/
          (PositionsTotal()+Account.numOrders()+1)<Account.LimitOrders()
          ;i-=CSymbol.LotsMax())
        {
         //--- normalize Lot-size
         NormalizeLotsize(i);
         /* Open buy-stop order with a Lot-size not more than max-lot and set order expiration
         to the Symbol's session end time for the current day.
         */
         if(!Trade.BuyStop((i>CSymbol.LotsMax())?CSymbol.LotsMax():i,myBuyStop.Get().Open,
                           CSymbol.GetSymbolName(),myBuyStop.Get().Stop,myBuyStop.Get().Take,
                           ORDER_TIME_SPECIFIED,CTS.SessionEnd(),COMMENT))
           {
            //--- Order failed to open
            return false;
           }
        }
     }
   else
     {
      //--- Order failed
      return false;
     }
//--- Return trade result.
   return true;
  }

步骤1:设置买入止损订单属性

SetBuyStop(SL, TP);

  • 此行调用SetBuyStop 方法(前文已解释)来计算并设置买入止损订单的开仓价格、止损和止盈。
  • 结果存储在myBuyStop对象中,该对象包含买入止损订单的关键属性(开仓价格、止损和止盈)。

步骤2:为风险管理设置订单类型

SetOrderType(ORDER_TYPE_BUY);

  • 将内部订单类型设置为ORDER_TYPE_BUY。尽管这是一个买入止损订单,但它本质上仍然是一个买入订单,此设置用于计算风险管理指标,如止损、止盈和仓位规模。

步骤3:为风险管理设置开仓和平仓价格

OpenPrice = myBuyStop.Get().Open;
ClosePrice = myBuyStop.Get().Stop;

  • OpenPrice:为设置的买入止损订单计算开仓价格(myBuyStop.Get().Open)。
  • ClosePrice:为设置的买入止损订单计算止损价格(myBuyStop.Get().Stop)。

步骤4:设置magic数字

Trade.SetExpertMagicNumber(Magic);

  • 使用Trade.SetExpertMagicNumber为订单设置magic数字。使用magic数字作为订单唯一标识,这在管理由EA开立的交易时非常有用。

步骤5:检查已开仓或已成交订单

if (!OpenOrder(ORDER_TYPE_BUY_STOP, Magic, COMMENT) && !OpenedDeal(DEAL_TYPE_BUY, Magic, COMMENT)

  • OpenOrder:检查是否已存在具有相同magic数字和注释的待定买入止损订单。如果存在,则函数将跳过开立新订单。
  • OpenedDeal:检查是否已存在使用相同magic数字和注释的买入仓位。如果存在这样的交易,则函数将避免开立新订单。

步骤6:验证买入止损订单属性

&& Valid_Order(ORDER_TYPE_BUY_STOP, myBuyStop.Get().Open, myBuyStop.Get().Stop, myBuyStop.Get().Take))

  • 使用Valid_Order方法(前文已解释)检查计算出的买入止损订单属性是否有效。该方法检查开仓价格、止损和止盈是否正确,以及订单是否符合交易品种的规则(例如,最小止损水平、点值等)。

步骤7:遍历交易量大小

for (double i = Volume(); i >= CSymbol.LotsMin() && (PositionsTotal() + Account.numOrders() + 1) < Account.LimitOrders(); i -= CSymbol.LotsMax())

  • Volume():检索订单的当前交易量(手数大小)。
  • 循环从当前手数大小(i = Volume())开始,只要满足以下条件:

    1. 手数大小大于或等于最小手数大小(CSymbol.LotsMin())。
    2. 总仓位数加上账户的现有订单数小于账户的订单限制(Account.LimitOrders())。

在每次迭代中以最大手数大小(CSymbol.LotsMax())的增量减少手数大小,以确保订单符合手数限制。

步骤8:标准化手数大小

NormalizeLotsize(i);

  • 对手数大小(i)进行标准化处理,确保其调整为符合交易品种的精度规则。

步骤 9:尝试开立买入止损订单

if (!Trade.BuyStop((i > CSymbol.LotsMax()) ? CSymbol.LotsMax() : i, myBuyStop.Get().Open, CSymbol.GetSymbolName(), 
myBuyStop.Get().Stop, myBuyStop.Get().Take, ORDER_TIME_SPECIFIED, CTS.SessionEnd(), COMMENT))

  • Trade.BuyStop():尝试放置包含以下参数的买入止损订单:

    1. 手数大小:手数大小上限为 CSymbol.LotsMax()(交易品种允许的最大手数大小)。如果(i)大于最大手数大小,则使用最大值;否则,使用当前手数大小(i)。
    2. 开仓价格:买入止损订单应执行的价格。
    3. 交易品种名称:交易品种的名称(货币对等)。
    4. 止损:计算出的订单止损价格。
    5. 止盈:计算出的订单止盈价格。
    6. 订单过期时间:订单设置为在当前日交易品种的交易时段结束时过期(CTS.SessionEnd())。
    7. 注释:前文提供的可选注释。

如果订单未能开立,函数返回false,表示未成功放置买入止损订单。

步骤10:返回结果

return true;

  • 如果订单成功开立,函数返回true,表示已成功放置买入止损订单。

OpenSellStop函数

OpenSellStop函数的逻辑与前文解释的OpenBuyStop函数类似。

OpenStops函数尝试同时开立买入止损和卖出止损订单。 

//+------------------------------------------------------------------+
//|This function will open both buy-stop and sell-stop orders for    |
//|StopOrdersType(STOP ORDERS)                                       |
//+------------------------------------------------------------------+
bool CTradeManagement::OpenStops(int SL,int TP,ulong Magic,string COMMENT=NULL)
  {
//--- Set buy-stop properties
   SetBuyStop(SL,TP);
//--- Set sell-stop properties
   SetSellStop(SL,TP);
//--- Set the order type for Risk management calculation
   SetOrderType(ORDER_TYPE_BUY);
//--- Set open price for Risk management calculation
   OpenPrice = myBuyStop.Get().Open;
//--- Set close price for Risk management calculation
   ClosePrice = myBuyStop.Get().Stop;
//--- Set Trade magic number
   Trade.SetExpertMagicNumber(Magic);
//--- Check if there are any open trades or opened deals or canceled deals already
   if(!OpenOrder(ORDER_TYPE_BUY_STOP,Magic,COMMENT)&&!OpenedDeal(DEAL_TYPE_BUY,Magic,COMMENT)
      &&!OpenedDeal(DEAL_TYPE_BUY_CANCELED,Magic,COMMENT)
//--- Check if the buy-stop properties are valid
      &&Valid_Order(ORDER_TYPE_BUY_STOP,myBuyStop.Get().Open,myBuyStop.Get().Stop,myBuyStop.Get().Take)
      &&!OpenOrder(ORDER_TYPE_SELL_STOP,Magic,COMMENT)
      &&!OpenedDeal(DEAL_TYPE_SELL,Magic,COMMENT)&&!OpenedDeal(DEAL_TYPE_SELL_CANCELED,Magic,COMMENT)
//--- Check if the sell-stop properties are valid
      &&Valid_Order(ORDER_TYPE_SELL_STOP,mySellStop.Get().Open,mySellStop.Get().Stop,mySellStop.Get().Take))
     {
      //--- Iterate through the Lot-sizes if they're more than max-lot
      for(double i=Volume();i>=CSymbol.LotsMin()&&
          /* Check if current number of orders +2 more orders is less than
             account orders limit.*/
          (PositionsTotal()+Account.numOrders()+2)<Account.LimitOrders()
          ;i-=CSymbol.LotsMax())
        {
         //--- normalize Lot-size
         NormalizeLotsize(i);
         /* Open orders with a Lot-size not more than max-lot and set order expiration
         to the Symbol's session end time for the current day.
         */
         if(!Trade.BuyStop((i>CSymbol.LotsMax())?CSymbol.LotsMax():i,myBuyStop.Get().Open,
                           CSymbol.GetSymbolName(),myBuyStop.Get().Stop,myBuyStop.Get().Take,
                           ORDER_TIME_SPECIFIED,CTS.SessionEnd(),COMMENT)
            ||!Trade.SellStop((i>CSymbol.LotsMax())?CSymbol.LotsMax():i,mySellStop.Get().Open,
                              CSymbol.GetSymbolName(),mySellStop.Get().Stop,mySellStop.Get().Take,
                              ORDER_TIME_SPECIFIED,CTS.SessionEnd(),COMMENT))
           {
            //--- one or more orders failed to open.
            return false;
           }
        }
     }
   else
     {
      //--- Orders failed
      return false;
     }
//--- Return trade result.
   return true;
  }

步骤1:设置买入止损和卖出止损订单属性

SetBuyStop(SL, TP);
SetSellStop(SL, TP);

  • SetBuyStop:此方法计算并设置买入止损订单的开仓价格、止损和止盈。这些值存储在myBuyStop对象中。
  • SetSellStop:类似地,此方法计算并设置卖出止损订单的开仓价格、止损和止盈,存储在mySellStop对象中。

步骤2:为风险管理设置订单类型

SetOrderType(ORDER_TYPE_BUY);

  • 将内部订单类型设置为买入订单,用于风险管理计算,尽管同时要放置买入止损和卖出止损订单。此设置用于后续基于买入订单的止损和止盈来评估风险。

步骤3:为风险管理设置开仓和平仓价格

OpenPrice = myBuyStop.Get().Open;
ClosePrice = myBuyStop.Get().Stop;

  • OpenPrice:计算出的买入止损订单的开仓价格。
  • ClosePrice:计算出的买入止损订单的止损价格。这些价格用于内部风险管理计算。

步骤4:设置magic数字

Trade.SetExpertMagicNumber(Magic);

  • 这为交易分配magic数字,用于唯一标识由EA管理的订单。

步骤5:检查现有订单或交易

if(!OpenOrder(ORDER_TYPE_BUY_STOP, Magic, COMMENT) && !OpenedDeal(DEAL_TYPE_BUY, Magic, COMMENT)
   && !OpenedDeal(DEAL_TYPE_BUY_CANCELED, Magic, COMMENT)

  • OpenOrder:检查是否已存在具有相同magic数字和注释的待定买入止损订单。如果存在,则不会开立新的买入止损订单。
  • OpenedDeal:检查是否已存在具有相同magic数字和注释的活跃或已取消的买入仓位。如果存在,则不会放置新的买入止损订单。

步骤6:验证买入止损订单属性

&& Valid_Order(ORDER_TYPE_BUY_STOP, myBuyStop.Get().Open, myBuyStop.Get().Stop, myBuyStop.Get().Take)

  • Valid_Order:此方法验证买入止损订单属性(开仓价格、止损、止盈),以确保它们符合交易品种的规则(例如,最小止损水平)。如果验证通过,则继续检查卖出止损订单。

步骤7:检查现有卖出订单或交易

&& !OpenOrder(ORDER_TYPE_SELL_STOP, Magic, COMMENT) && !OpenedDeal(DEAL_TYPE_SELL, Magic, COMMENT)
&& !OpenedDeal(DEAL_TYPE_SELL_CANCELED, Magic, COMMENT)

  • 与买入止损检查类似,这些条件检查是否已存在具有相同magic数字和注释的卖出止损订单、活跃的卖出交易或已取消的卖出交易。如果存在,则函数将避免放置新的卖出止损订单。

步骤8:验证卖出止损订单属性

&& Valid_Order(ORDER_TYPE_SELL_STOP, mySellStop.Get().Open, mySellStop.Get().Stop, mySellStop.Get().Take))

  • Valid_Order:此方法验证卖出止损订单属性(开仓价格、止损、止盈)。如果验证通过,则继续开立买入止损和卖出止损订单。

步骤9:遍历手数大小

for (double i = Volume(); i >= CSymbol.LotsMin() && (PositionsTotal() + Account.numOrders() + 2) < Account.LimitOrders(); i -= CSymbol.LotsMax())

  • Volume():检索当前交易量(手数大小)。
  • 循环从完整手数大小(i = Volume())开始,如果手数大小超过最大允许手数大小(CSymbol.LotsMax()),则减小手数大小(i -= CSymbol.LotsMax())。
  • 确保开设的仓位总数和待定的订单数在账户的限制范围内(Account.LimitOrders())。

循环确保如果手数大小超过交易品种允许的最大手数大小,将把订单拆分为多个较小的订单。

步骤10:标准化手数大小

NormalizeLotsize(i);

  • 对手数大小(i)进行标准化处理,以确保其符合交易品种允许的精度。

步骤11:尝试开立买入止损和卖出止损订单

if(!Trade.BuyStop((i > CSymbol.LotsMax()) ? CSymbol.LotsMax() : i, myBuyStop.Get().Open,
                  CSymbol.GetSymbolName(), myBuyStop.Get().Stop, myBuyStop.Get().Take,
                  ORDER_TIME_SPECIFIED, CTS.SessionEnd(), COMMENT)
   || !Trade.SellStop((i > CSymbol.LotsMax()) ? CSymbol.LotsMax() : i, mySellStop.Get().Open,
                     CSymbol.GetSymbolName(), mySellStop.Get().Stop, mySellStop.Get().Take,
                     ORDER_TIME_SPECIFIED, CTS.SessionEnd(), COMMENT))

  • Trade.BuyStop():尝试放置包含以下参数的买入止损订单:
    • 手数大小:如果当前手数大小(i)超过最大允许手数(CSymbol.LotsMax()),则以最大允许手数大小放置订单。
    • 开仓价格:计算出的买入止损订单的开仓价格。
    • 交易品种名称:交易工具的名称。
    • 止损:买入止损订单的止损价格。
    • 止盈:买入止损订单的止盈价格。
    • 到期时间:设置为当前交易日交易时段结束时间(CTS.SessionEnd())。
    • 注释:用于描述订单的可选注释。
  • Trade.SellStop:类似地,尝试以与买入止损订单相同的逻辑放置卖出止损订单。

如果任一订单未能成功开立,函数将返回false。

步骤12:返回结果

return true;

  • 如果买入止损和卖出止损订单均成功开立,函数将返回true。如果过程中的任何环节失败,函数将返回false。

CloseTrades函数旨在关闭所有与指定注释(COMMENT_COMMON)匹配的未平仓位。 

//+------------------------------------------------------------------+
//|Function will attempt to close all trades depending on the        |
//|position comment                                                  |
//+------------------------------------------------------------------+
void CTradeManagement::CloseTrades(string COMMENT_COMMON)
  {
//--- Iterate through all open positions
   for(int i=0; i<PositionsTotal(); i++)
     {
      ulong ticket = PositionGetTicket(i);
      //--- Check if Position ticket is above zero
      if(ticket>0)
        {
         //--- Check if the Position's Symbol,Comment is correct
         if(PositionGetString(POSITION_SYMBOL)==CSymbol.GetSymbolName()
            &&StringFind(PositionGetString(POSITION_COMMENT),COMMENT_COMMON)>=0)
           {
            //--- close trade.
            Trade.PositionClose(ticket);
           }
        }
     }
  }

步骤1:遍历所有未平仓位

for (int i = 0; i < PositionsTotal(); i++)

  • PositionsTotal():返回已开仓位的总数。
  • 此循环遍历当前所有未平仓位。变量i是仓位的索引,范围从0到 PositionsTotal() - 1。

步骤2:获取仓位的订单编号

ulong ticket = PositionGetTicket(i);

  • PositionGetTicket(i):获取索引i处未平仓位的订单编号。订单编号是每个仓位的唯一标识符。
  • 订单编号存储在变量ticket中。

步骤3:检查订单编号是否有效

if (ticket > 0)

  • 此检查确保订单编号大于0,在继续执行之前验证获取的订单编号是否有效。订单编号为0表示在给定索引处不存在仓位,这不是有效状态。

步骤4:验证仓位交易品种和注释

if (PositionGetString(POSITION_SYMBOL) == CSymbol.GetSymbolName() 
    && StringFind(PositionGetString(POSITION_COMMENT), COMMENT_COMMON) >= 0)

  • PositionGetString(POSITION_SYMBOL):获取索引i处仓位的交易品种名称(例如,货币对)。
  • CSymbol.GetSymbolName():获取与CSymbol对象关联的交易品种名称。
  • 第一个条件检查仓位的交易品种是否与CSymbol管理的交易品种相匹配。
  • PositionGetString(POSITION_COMMENT):获取附加到未平仓位的注释字符串。
  • StringFind(PositionGetString(POSITION_COMMENT), COMMENT_COMMON):检查指定的COMMENT_COMMON字符串是否存在于仓位的注释中。
    • 如果注释包含COMMENT_COMMON,函数将返回匹配开始的索引。
    • 条件 >= 0 确保仅在注释包含子字符串时通过。

这样确保仅选择交易品种和注释匹配的仓位进行平仓。

步骤5:平仓

Trade.PositionClose(ticket);

  • Trade.PositionClose(ticket):尝试通过订单编号平仓未平仓位。
  • 如果仓位符合条件(交易品种正确且包含指定注释),则使用此方法平仓。


结论

在本文中,我们实现了用于开立止损订单的代码,并在开立订单之前检查交易和订单的有效性。我们创建了一个名为FundamentalMode的函数,该函数通过管理相反的止损订单来处理一种特殊的交易模式。此外,还实现了止损订单的滑点减少功能,以减轻新闻发布导致市场波动期间的价格滑点风险。

要点:

  • 执行精准度:交易管理类负责处理下单、修改订单和平仓的所有方面,确保即使在市场波动的情况下,交易执行也能保持精准。
  • 实时调整:能够动态调整止损和止盈,确保EA能够对实时市场变化做出响应,从而更好地实现风险管理。
  • 滑点管理:通过考虑滑点并实施动态调整交易参数的逻辑,交易管理类确保交易尽可能接近预期条件执行,从而减少潜在损失。

感谢您的阅读,我期待在下一篇文章中为您提供更多有价值的内容。 

本文由MetaQuotes Ltd译自英文
原文地址: https://www.mql5.com/en/articles/16169

附加的文件 |
NewsTrading_Part5.zip (598.37 KB)
最近评论 | 前往讨论 (6)
Kabelo Frans Mampa
Kabelo Frans Mampa | 7 11月 2024 在 13:30
Hamid Rabia #:

你好,卡贝罗

非常有趣

不幸的是,压缩文件(NewsTrading_Part5.zip )与第 4 条中的压缩文件(NewsTrading_Part4.zip )相同?

嗨,Hamid,很遗憾,我不能把所有代码都放在一篇文章里。新闻第 5 部分的代码比第 4 部分多,但并不完整。在第 6 部分中,我们将执行剩余的代码,使所有代码都能一起工作。

感谢您的时间和理解。

Hamid Rabia
Hamid Rabia | 26 11月 2024 在 19:02

您好、

有没有简单的方法在新闻之前显示订单类型,买入、卖出或净值?

谢谢。

Kabelo Frans Mampa
Kabelo Frans Mampa | 26 11月 2024 在 21:38
Hamid Rabia 订单类型(买入、卖出或净值)?

谢谢。

您好,Hamid Rabia,感谢您的建议。我一定会找到解决方案,并在完成后给您发送私信。

Veeral10
Veeral10 | 3 12月 2024 在 22:27

您写的这篇文章非常精彩。


期待看到第六部分和完整的代码。

Kabelo Frans Mampa
Kabelo Frans Mampa | 4 12月 2024 在 13:59
Veeral10 #:

您写的这篇文章非常精彩。


期待看到第六部分和完整的代码。

您好 Veeral10,感谢您的赞誉!

交易中的神经网络:广义 3D 引用表达分段 交易中的神经网络:广义 3D 引用表达分段
在分析市场状况时,我们将其切分为不同的段落,标识关键趋势。然而,传统的分析方法往往只关注一个层面,从而限制了正确的感知。在本文中,我们将学习一种方法,可选择多个对象,以确保对形势进行更全面、及多层次的理解。
基于MQL5和Python的自优化EA(第六部分):利用深度双重下降算法 基于MQL5和Python的自优化EA(第六部分):利用深度双重下降算法
传统的机器学习教导从业者要警惕不要使模型陷入过度拟合。然而,这种观念正受到哈佛大学研究人员最新发表的学术见解的挑战。他们发现,看似过拟合的情形在某些情况下可能是由于提前终止训练过程导致的。我们将展示如何利用研究论文中发表的观点,来改进我们使用人工智能预测市场行为的方式。
开发多币种 EA 交易(第 18 部分):考虑远期的自动化组选择 开发多币种 EA 交易(第 18 部分):考虑远期的自动化组选择
让我们继续将之前手动执行的步骤自动化。这一次,我们将回到第二阶段的自动化,即选择交易策略的最佳单实例组,并补充考虑远期实例结果的能力。
开发回放系统(第 66 部分):玩转服务(七) 开发回放系统(第 66 部分):玩转服务(七)
在本文中,我们将实现第一个解决方案,该解决方案使我们能够确定何时在图表上出现新的柱形。此解决方案适用于各种情况。了解它的发展将有助于你掌握几个重要方面。此处提供的内容仅用于教育目的。在任何情况下,除了学习和掌握所提出的概念外,都不应出于任何目的使用此应用程序。