文章 "解密开盘区间突破(ORB)日内交易策略"

 

新文章 解密开盘区间突破(ORB)日内交易策略已发布:

开盘区间突破(ORB)策略基于这样一种理念:市场开盘后不久确立的初始交易区间,反映了买卖双方就价格价值达成共识的重要水平。通过识别突破某一特定区间上方或下方的走势,交易者可以把握随之而来的市场契机——当市场方向愈发明朗时,这种契机往往会进一步显现。本文将探讨三种源自康克瑞图姆集团(Concretum Group)改良的ORB策略。

开盘区间突破(ORB)策略基于这样一种理念:市场开盘后不久确立的初始交易区间,反映了买卖双方就价格价值达成共识的重要水平。通过识别突破某一特定区间上方或下方的走势,交易者可以把握随之而来的市场契机——当市场方向愈发明朗时,这种契机往往会进一步显现。 

在本文中,我们将探讨三种改编自康克瑞图姆集团论文的ORB策略。首先,我们将介绍研究背景,包括关键概念和所采用的研究方法。接着,针对每种策略,我们将解释其运作方式,列出信号规则,并从统计角度分析其表现。最后,我们将从投资组合的角度审视这些策略,重点关注分散化这一主题。  

本文不会深入探讨编程细节,而是聚焦于研究过程,包括重现、分析和测试这三篇论文中的策略。这将适合那些寻找潜在交易优势或对如何研究并复制这些策略感兴趣的读者。尽管如此,我们仍会公布所有智能交易系统(EA)的MQL5代码。欢迎读者们基于这些框架自行进行拓展开发。


作者:Zhuo Kai Chen

 
精彩文章

非常值得任何交易者学习

我的亲身经历
 
Timmy T #:
好文章

非常值得任何交易者学习

我的亲身经历

谢谢你,蒂米。

 

确实是篇好文章,感谢您花费时间和精力编写代码并与社区分享。

我有一个简单的问题:"在我们正在调整的研究中,他们专注于在市场开盘和收盘(东部时间上午 9:30 至下午 4:00)之间进行交易的策略。由于我们的经纪商使用 UTC+2/3,这意味着服务器时间 为 18:30-24:00,请确保在测试时根据您自己经纪商的时区进行调整

您能告诉我您的时间转换思路吗?

使用通用时间转换器,我将东部时间 09:30 转换为 mt5 GMT+2 时的16: 30,将 mt5 GMT+3 时的17: 30。

感谢您的帮助,再次感谢。

附加的文件:
NY_GMT.png  12 kb
 
Sunjoo 服务器时间 18:30-24:00,请确保在测试时根据您自己经纪商的时区进行调整 "

您能告诉我时间转换的思路吗?

使用通用时间转换器 - 我将东部时间 09:30 转换为 mt5 GMT+2 时的16: 30,将 mt5 GMT+3 时的17: 30。

感谢您的帮助,再次感谢。

您说得对,我错误地将文章中的服务器时间转换为纽约股市开盘时间。应该是 17:30,而不是 18:30。因此,您可以认为我在文章中的所有策略规则都应该在开市后 1 小时进行交易。感谢您的指出,很抱歉造成您的困惑。

 

感谢您的发布,非常有趣!

关于代码的一个问题:

在你的 MarketOpened 函数中,你使用了

"if (currentHour >= startHour && currentMinute>=startMinute)return true;" <- 这看起来就像是如果市场开盘,它将只交易一小时的部分时间,因为如果你在每小时的开始,即使市场开盘,它也会返回假值。 它只在分钟数为 0 时起作用,而这并不是市场交易时段的开始。

 
Digitus #:

感谢您的发布,非常有趣!

关于代码的一个问题:

在您的 MarketOpened 函数中,您使用了:

"if (currentHour >= startHour && currentMinute>=startMinute)return true;" <- 这看起来像是如果市场开盘,它将只交易一小时内的部分时间,因为如果你在每小时的开始,即使市场开盘,它也会返回假值。 它只在分钟数为 0 时起作用,而这不是市场交易时段的开始。

天哪,我居然漏掉了这一点。应该是这样的:

    if ((currentHour >= startHour &&currentMinute>=startMinute)||currentHour>startHour)return true;

对于这个粗心的错误,我真的很抱歉。感谢您仔细阅读并指出来。

 

注意。在 ORB3 中,我将开市时间硬编码为 9:30。您可以将其更改为这些函数,以便与纽约市场开盘时间的服务器时间 相匹配。

//+------------------------------------------------------------------+
// | 获取混凝土带的上限值|
//+------------------------------------------------------------------+
double getUpperBand(int target_hour = 17, int target_min = 30) {
    // 获取当前条形图的时间
    datetime current_time = iTime(_Symbol, PERIOD_CURRENT, 0);
    MqlDateTime current_dt;
    TimeToStruct(current_time, current_dt);
    int current_hour = current_dt.hour;
    int current_min = current_dt.min;
    
    // 在目标时间(如服务器时间 17:30)查找今日开盘价
    datetime today_start = iTime(_Symbol, PERIOD_D1, 0);
    int bar_at_target_today = getBarShiftForTime(today_start, target_hour, target_min);
    if (bar_at_target_today < 0) return 0; // 如果不存在目标条形图,则返回 0
    double open_target_today = iOpen(_Symbol, PERIOD_M1, bar_at_target_today);
    if (open_target_today == 0) return 0; // 没有有效价格
    
    // 根据过去 14 天计算西格玛值
    double sum_moves = 0;
    int valid_days = 0;
    for (int i = 1; i <= 14; i++) {
        datetime day_start = iTime(_Symbol, PERIOD_D1, i);
        int bar_at_target = getBarShiftForTime(day_start, target_hour, target_min);
        int bar_at_HHMM = getBarShiftForTime(day_start, current_hour, current_min);
        if (bar_at_target < 0 || bar_at_HHMM < 0) continue; // 如果条形图不存在,则跳过
        double open_target = iOpen(_Symbol, PERIOD_M1, bar_at_target);
        double close_HHMM = iClose(_Symbol, PERIOD_M1, bar_at_HHMM);
        if (open_target == 0) continue; // 如果没有有效的开盘价,则跳过
        double move = MathAbs(close_HHMM / open_target - 1);
        sum_moves += move;
        valid_days++;
    }
    if (valid_days == 0) return 0; // 如果没有有效数据,则返回 0
    double sigma = sum_moves / valid_days;
    
    // 计算上限
    double upper_band = open_target_today * (1 + sigma);
    
    // 在上波段水平绘制一个蓝点
    string obj_name = "UpperBand_" + TimeToString(current_time, TIME_DATE|TIME_MINUTES|TIME_SECONDS);
    ObjectCreate(0, obj_name, OBJ_ARROW, 0, current_time, upper_band);
    ObjectSetInteger(0, obj_name, OBJPROP_ARROWCODE, 159); // 圆点符号
    ObjectSetInteger(0, obj_name, OBJPROP_COLOR, clrBlue);
    ObjectSetInteger(0, obj_name, OBJPROP_WIDTH, 2);
    
    return upper_band;
}

//+------------------------------------------------------------------+
//| 获取 Concretum 波段下限值|
//+------------------------------------------------------------------+
double getLowerBand(int target_hour = 17, int target_min = 30) {
    // 获取当前条形图的时间
    datetime current_time = iTime(_Symbol, PERIOD_CURRENT, 0);
    MqlDateTime current_dt;
    TimeToStruct(current_time, current_dt);
    int current_hour = current_dt.hour;
    int current_min = current_dt.min;
    
    // 在目标时间(如服务器时间 17:30)查找今日开盘价
    datetime today_start = iTime(_Symbol, PERIOD_D1, 0);
    int bar_at_target_today = getBarShiftForTime(today_start, target_hour, target_min);
    if (bar_at_target_today < 0) return 0; // 如果不存在目标条形图,则返回 0
    double open_target_today = iOpen(_Symbol, PERIOD_M1, bar_at_target_today);
    if (open_target_today == 0) return 0; // 没有有效价格
    
    // 根据过去 14 天计算西格玛值
    double sum_moves = 0;
    int valid_days = 0;
    for (int i = 1; i <= 14; i++) {
        datetime day_start = iTime(_Symbol, PERIOD_D1, i);
        int bar_at_target = getBarShiftForTime(day_start, target_hour, target_min);
        int bar_at_HHMM = getBarShiftForTime(day_start, current_hour, current_min);
        if (bar_at_target < 0 || bar_at_HHMM < 0) continue; // 如果条形图不存在,则跳过
        double open_target = iOpen(_Symbol, PERIOD_M1, bar_at_target);
        double close_HHMM = iClose(_Symbol, PERIOD_M1, bar_at_HHMM);
        if (open_target == 0) continue; // 如果没有有效的开盘价,则跳过
        double move = MathAbs(close_HHMM / open_target - 1);
        sum_moves += move;
        valid_days++;
    }
    if (valid_days == 0) return 0; // 如果没有有效数据,则返回 0
    double sigma = sum_moves / valid_days;
    
    // 计算下限值
    double lower_band = open_target_today * (1 - sigma);
    
    // 在较低波段水平绘制一个红点
    string obj_name = "LowerBand_" + TimeToString(current_time, TIME_DATE|TIME_MINUTES|TIME_SECONDS);
    ObjectCreate(0, obj_name, OBJ_ARROW, 0, current_time, lower_band);
    ObjectSetInteger(0, obj_name, OBJPROP_ARROWCODE, 159); // 圆点符号
    ObjectSetInteger(0, obj_name, OBJPROP_COLOR, clrRed);
    ObjectSetInteger(0, obj_name, OBJPROP_WIDTH, 2);
    
    return lower_band;
}

更改计算时间可以进一步优化策略。)

 
Zhuo Kai Chen #:

天哪,我居然错过了这个。应该是

我真的很抱歉粗心犯错。谢谢你仔细阅读并指出来。

不用担心,我已经将开市和闭市合并为一个功能。

bool MarketState()
{
   MqlDateTime structTime;
   TimeCurrent(structTime);
   structTime.sec = 0;
   structTime.hour = startHour;
   structTime.min = startMinute; 
   datetime timeStart = StructToTime(structTime);
   structTime.hour = endHour;
   structTime.min = endMinute;   
   datetime timeEnd = StructToTime(structTime);
   if(TimeCurrent() >= timeStart && TimeCurrent() < timeEnd)return true;
   else return false;
}


还有一件事:你使用经纪商的 OHLC 数据进行回测,没有延迟。这些回溯测试与使用真实刻度线数据进行的回溯测试相比似乎有点乐观,因为真实刻度线数据会随机延迟滑点和重新报价。

再次感谢您的努力!

 
Digitus #:

不用担心,我已经将开放市场和封闭市场合并为一个功能。


还有一件事:你使用经纪商 OHLC 数据进行回测,没有延迟。这些回溯测试与使用真实跳动点数据进行的回溯测试相比似乎有点乐观,因为跳动点和重新报价会有随机延迟。

再次感谢您的努力!

你的修改做得很好!我已经更新了 Github 上的所有代码。

对于您关心的问题,交易逻辑发生在每一个新的条形图中,不涉及跳动。此外,平均持有时间只有几个小时,我认为这不会造成严重的滑点问题。我认为很少有经纪商提供超过 5 年的真实刻度线数据,1 分钟的 OHLC 就足够了。

 
精彩文章