保持敬畏之心
交易是一场持久战

MQL4(42):创建简单的交易定时器

日期时间变量实际上是基于从1970年1月1日开始计算的秒数。比如,2009年6月15日凌晨0点会被记录为1245024000。这种日期时间格式的好处是,它使过去和将来时间的比较及数学运算变得相当简便。 例如,若想判断一个日期是在另一个日期之前还是之后,只需要进行简单的比较操作。假设开始时间为2009年6月15日下午2点,结束时间为2009年6月16日凌晨5点:

if(StartDate < EndDate) // 结果为真
if(StartDate > EndDate) // 结果为假

另一个优点是可以通过直接加减秒数来调整某个具体日期。如果你想给开始时间加上24小时,只需加上一天的秒数(86400秒):

datetime AddDay = StartDate + 86400;

如果你需要频繁处理日期时间变量的数学运算,定义一些整型常量来表示不同的时间单位会很有帮助:

#define SEC_H1 3600 // 表示一个小时的秒数
#define SEC_D1 86400 // 表示一天的秒数

不过,日期时间格式的不足之处在于它不易于人类阅读。看到如1245024000这样的数字,无法直观理解它对应的具体日期。这时就需要用到转换函数,将日期时间格式转换为更易读的形式。

日期时间常量通常按照以下格式表示:yyyy.mm.dd hh:mm。例如,2009年6月15日凌晨0点表示为2009.06.15 00:00。虽然存在多种日期时间常量格式,但我们将使用上述格式,因为它是唯一一种可以方便转换的格式。 要将日期时间变量转换为字符串形式,可以使用TimeToStr()函数。以下是其基本语法:

string TimeToStr(datetime Time, int Output = TIME_DATE|TIME_MINUTES);
  • Time:表示自1970年1月1日以来的秒数的日期时间变量。
  • Output:一个可选参数,用于指定输出格式,可以是仅日期、仅时间(小时和分钟)、或者包含秒在内的完整时间信息。有效值包括:
    • TIME_DATE:仅输出日期,例如2009.06.15
    • TIME_MINUTES:输出小时和分钟,例如05:30
    • TIME_SECONDS:输出完整的小时、分钟和秒,例如05:30:45

如果想得到默认的yyyy.mm.dd hh:mm格式的结果,可以省略Output参数。根据需求选择合适的参数值,比如下面的例子中,假设StartTime为2009年6月15日上午5点30分45秒:

TimeToStr(StartTime,TIME_DATE) // 返回 "2009.06.15"
TimeToStr(StartTime,TIME_SECONDS) // 返回 "05:30:45"
TimeToStr(StartTime,TIME_MINUTES) // 返回 "05:30"
TimeToStr(StartTime,TIME_DATE|TIME_SECONDS) // 返回 "2009.06.15 05:30:45"
TimeToStr(StartTime) // 返回 "2009.06.15 05:30"

为了构建一个日期时间常量,可以利用字符串拼接的方法,并使用StrToTime()函数将其转换为日期时间变量。该函数的用法与TimeToStr()相似,但不涉及Output参数。字符串必须遵循yyyy.mm.dd hh:mm格式以便正确转换。例如,使用如下代码可以将几个整数转换为字符串,再转换为日期时间变量:

extern int UseMonth = 6; // 使用月份
extern int UseDay = 15; // 使用日期
extern int UseHour = 5; // 使用小时
extern int UseMinute = 30; // 使用分钟

// 创建字符串常量
string DateConstant = StringConcatenate(Year(), ".", UseMonth, ".", UseDay, " ", UseHour, ":", UseMinute); // DateConstant 是 "2009.6.15 05:30"

// 转换为日期时间变量
datetime StartTime = StrToTime(DateConstant); // StartTime 是 "1245043800"

注意,在StringConcatenate()函数中,我们使用Year()函数返回当前年份,而不是使用外部变量。你可以使用类似Month()Day()等函数插入当前的时间值。

日期和时间函数有两种获取当前时间的函数:TimeCurrent()返回当前服务器时间,而TimeLocal()则返回本地计算机的时间。根据需求,你可以自由选择使用哪一个。为了灵活切换这两种时间源,可以定义一个布尔类型的外部变量:

extern bool UseLocalTime = true; // 使用本地时间标志

下面是如何根据这个变量将当前时间(无论是本地时间还是服务器时间)赋值给名为CurrentTime的变量:

if(UseLocalTime == true) 
    datetime CurrentTime = TimeLocal(); // 使用本地时间
else 
    CurrentTime = TimeCurrent(); // 使用服务器时间

有时候,你可能只需要获取当前时间的一部分信息,比如仅需知道当前的小时数或者某天的具体日期。以下是几个常用的函数,它们能够帮助你获取当前时间的相关部分。请注意,这些函数均基于服务器时间而非本地时间,其返回值均为整数类型:

  • Year() – 返回当前的四位数年份,例如2009。
  • Month() – 返回当前月份,范围是1至12。
  • Day() – 返回当前月份中的具体日期,范围是1至31。
  • DayOfWeek() – 返回当前星期几对应的整数值。其中,周日为0,周一为1,以此类推。
  • Hour() – 返回当前24小时制下的小时数,范围是0至23。例如,早上3点对应的是3,下午3点则是15。
  • Minute() – 返回当前的分钟数,范围是0至59。

同样地,对于任意一个日期时间变量,你可以通过一系列特定的函数提取上述信息。这些函数需要一个日期时间变量作为参数,并且操作方式与之前提到的函数相似。如果你想要从TimeLocal()的结果中获取时间值,可以直接将TimeLocal()的输出作为下面函数的参数:

  • TimeYear() – 获取指定日期时间值的四位数年份。
  • TimeMonth() – 获取指定日期时间值的月份,范围是1至12。
  • TimeDay() – 获取指定日期时间值中的具体日期,范围是1至31。
  • TimeDayOfWeek() – 获取指定日期时间值对应的星期几的整数值。其中,周日为0,周一为1,以此类推。
  • TimeHour() – 获取指定日期时间值在24小时制下的小时数,范围是0至23。
  • TimeMinute() – 获取指定日期时间值中的分钟数,范围是0至59。

举例来说,假定TimeLocal()返回的是2009年6月15日上午5点30分:

datetime CurrentTime = TimeLocal();
int GetMonth = TimeMonth(CurrentTime); // 返回6
int GetHour = TimeHour(CurrentTime); // 返回5
int GetWeekday = TimeDayOfWeek(CurrentTime); // 返回1表示周一

创建简单的定时器

在MQL4编程中,利用时间和日期功能,我们可以为专家顾问程序添加一个定时器。一些交易者倾向于只在一天中最为活跃的时段,如伦敦和纽约的交易时段,进行交易。而另一些人则可能希望避开市场波动较大时期,比如经济报告发布期间或非农就业数据(NFP)公布时刻。 实现定时器的基本思路是设定一个起始时间和一个结束时间。我们可以通过外部整型变量输入这些时间参数。接着,我们需要构造一个日期时间常量字符串,并将其转换成日期时间变量。之后,我们会将这个时间段与当前时间进行对比。如果当前时间位于该时间段内,则允许交易。 首先,我们需要定义一些外部变量来配置定时器,包括启用/禁用定时器的开关,以及选择使用本地时间或服务器时间等选项。同时,我们也需要为起始时间和结束时间分别设置月份、日期、小时和分钟:

核心思路是:通过外部参数让用户设定一个允许交易的开始时间和结束时间。EA在运行时会检查当前时间是否落在这个预设的时间区间内,以此来决定是否允许开立新的交易。

我们首先需要一个布尔型外部参数(例如 UseTradingTimer)作为总开关,来决定是否启用时间过滤功能。如果启用,还需要一系列外部参数让用户指定开始和结束的月份、日期、小时和分钟,以及选择使用本地计算机时间还是服务器时间进行比较。

// --- 假设在EA的外部参数部分已定义 ---
// extern bool UseTradingTimer = true;     // 是否启用交易时段过滤
// extern int  StartMonth      = 1;        // 开始月份 (1-12)
// extern int  StartDay        = 1;        // 开始日期 (1-31)
// extern int  StartHour       = 0;        // 开始小时 (0-23)
// extern int  StartMinute     = 0;        // 开始分钟 (0-59)
// extern int  EndMonth        = 12;       // 结束月份 (1-12)
// extern int  EndDay          = 31;       // 结束日期 (1-31)
// extern int  EndHour         = 23;       // 结束小时 (0-23)
// extern int  EndMinute       = 59;       // 结束分钟 (0-59)
// extern bool UseLocalTime    = false;    // true: 使用本地时间; false: 使用服务器时间

// --- 在主函数内部 ---
// bool TradeAllowed; // 假设已在OnTick()函数开头声明

if (UseTradingTimer == true) // 如果启用了交易时段过滤
{
    // 1. 构建并转换开始时间
    string startDateTimeString = StringFormat("%04d.%02d.%02d %02d:%02d",
                                             Year(), StartMonth, StartDay, StartHour, StartMinute);
    datetime startTime = StrToTime(startDateTimeString);

    // 2. 处理结束年份(考虑跨年情况)
    int endYear = Year();
    // 如果开始时间是12月31日,并且结束时间设置在1月份,则结束年份应为下一年
    if (StartMonth == 12 && StartDay == 31 && EndMonth == 1)
    {
        endYear = Year() + 1;
    }

    // 3. 构建并转换结束时间
    string endDateTimeString = StringFormat("%04d.%02d.%02d %02d:%02d",
                                           endYear, EndMonth, EndDay, EndHour, EndMinute);
    datetime endTime = StrToTime(endDateTimeString);

    // 4. 获取当前时间 (本地时间或服务器时间)
    datetime currentTimeToCompare;
    if (UseLocalTime == true)
    {
        currentTimeToCompare = TimeLocal();
    }
    else
    {
        currentTimeToCompare = TimeCurrent(); // 服务器时间 (推荐用于交易逻辑)
    }

    // 5. 判断当前时间是否在允许的区间内
    //    条件: 开始时间 <= 当前时间 < 结束时间 (结束时间通常被视为开区间上限)
    if (startTime <= currentTimeToCompare && endTime > currentTimeToCompare)
    {
        TradeAllowed = true; // 在允许的时间段内
    }
    else
    {
        TradeAllowed = false; // 不在允许的时间段内
    }
}
else // 如果未启用交易时段过滤
{
    TradeAllowed = true; // 始终允许交易
}
  • 代码解释
    • 首先,我们将用户设定的开始年、月、日、时、分参数通过 StringConcatenate(或 StringFormat)组合成一个标准日期时间格式的字符串,然后使用 StrToTime() 函数将其转换为 MQL 的 datetime 类型变量 startTime
    • 跨年处理:一个特殊情况是当设定的时间段跨越年份时(例如,从12月31日到下一年的1月份)。代码中有一个检查:如果开始月份是12月、开始日期是31日,并且结束月份是1月,那么结束年份 endYear 会自动设置为当前年份加1。否则,结束年份使用当前年份。
    • 同样地,结束时间也被转换为 datetime 变量 endTime
    • 根据 UseLocalTime 参数的选择,获取当前的比较时间 currentTimeToCompare(本地计算机时间 TimeLocal() 或交易服务器时间 TimeCurrent())。对于交易逻辑,通常推荐使用服务器时间 TimeCurrent() 以避免时区问题。
    • 最后,通过 if (startTime <= currentTimeToCompare && endTime > currentTimeToCompare) 判断当前时间是否位于设定的开始时间和结束时间之间。如果是,则将布尔变量 TradeAllowed 设置为 true;否则设为 false
    • 如果 UseTradingTimer 本身就为 false(即不启用时间过滤),则 TradeAllowed 直接设为 true
  1. 在交易逻辑中应用 TradeAllowed 确定了 TradeAllowed 的状态后,我们就可以在执行实际的开仓、平仓等交易操作之前,用它来控制是否继续执行。最简单的方法是将所有核心的交易下单逻辑包裹在一个 if(TradeAllowed == true) 的代码块中:

    // --- 在主函数中,获取交易信号并准备下单的部分 ---
    
    // 首先执行上面的时间过滤逻辑,得到 TradeAllowed 的值
    
    if (TradeAllowed == true) // 只有当允许交易时,才执行下面的操作
    {
        // 例如,买入条件判断
        // (假设 FastMA, SlowMA, g_BuyTicket, BuyOrderCount() 等已定义)
        if (FastMA > SlowMA && g_BuyTicket == 0 && BuyOrderCount(Symbol(), MagicNumber) == 0)
        {
            // ... 执行买入开仓的代码(为简洁省略) ...
        }
    
        // 例如,卖出条件判断
        // (假设 g_SellTicket, SellOrderCount() 等已定义)
        if (FastMA < SlowMA && g_SellTicket == 0 && SellOrderCount(Symbol(), MagicNumber) == 0)
        {
            // ... 执行卖出开仓的代码(为简洁省略) ...
        }
    }
    // else (如果 TradeAllowed 为 false)
    // {
    //    Print("当前不在允许的交易时间段内。");
    // }
    

上述实现只是一个基础的日期和时间范围定时器。您还可以根据具体需求创建更复杂的定时逻辑,例如:

  • 仅在每周的特定几天(例如周一到周五)进行交易。
  • 设置相对于当天某个特定时间点(例如,只在亚洲时段、伦敦时段或纽约时段的特定几小时内)的交易窗口。
  • 组合日期范围和每日时间范围。

这些更高级的定时器逻辑,就需要您根据 MQL 提供的日期时间函数(如 DayOfWeek(), TimeHour(), TimeDayOfYear() 等)自行探索和构建了。

赞(0)
未经允许不得转载:图道交易 » MQL4(42):创建简单的交易定时器
分享到