大家好!今天我想和大家分享一个非常实用的图形指标,它基于斜率方向趋势指标,经过一些小修改后,可以在屏幕上以图形面板的形式展示所有时间框架的趋势。
虽然市面上已经有多个版本的这个指标,但没有一个能在一个图形面板中显示所有时间框架的趋势。我对这个指标进行了改进,使其能够更好地服务于交易者。
如果你对这个指标感兴趣,可以在这里查找更多信息:斜率方向线搜索.
我增加了一个功能,允许用户选择第二个移动平均平滑的方法。原来的代码使用线性加权法进行第二次计算,而我则提供了选择平滑方法的选项。使用线性加权法会使得指标过于灵敏,可能会给出错误的当前趋势信号。而如果使用简单平均法作为第二次计算,则能够得到一个更符合价格走势的趋势。
另外一个改进是,不仅能获取趋势的方向(上涨、下跌或平稳),还能够获取其数值。我会在接下来的文章中,详细介绍如何利用双斜率方向指标创建一个EA(专家顾问)。

接下来,我们快速看一下代码的实现。首先,我定义了所有将要使用的对象名称。这是一个良好的编程习惯,因为每次在代码中引号中插入对象名称时,它会为该对象分配空间。因此,只需通过定义语句定义一次,便可节省代码空间。
// 定义趋势方向 #define UPTREND 1 #define DOWNTREND -1 #define FLATTREND 0 // 定义对象 #define TrendPanel "TrendPanel" #define InfoLine1 "InfoLine1" #define InfoLine2 "InfoLine2" #define TrendLabel "趋势: M1 M5 M15 M30 H1 H4 D1 W1 MN" #define TrendUp "\233" #define TrendDown "\234" #define TrendFlat "\232" #define TrendUnknown "\251" #define StatObjectError "%s(%d) 创建'%s'失败. 错误 = %d"
接下来,我定义了两个数组。一个是每个时间框架的像素位置,另一个是时间框架。这些都被定义为全局变量。
// 定义全局变量 int TrendPosition[] = { 44, 64, 88, 114, 136, 156, 174, 194, 216 }; int TrendPeriods[] = { PERIOD_M1, PERIOD_M5, PERIOD_M15, PERIOD_M30, PERIOD_H1, PERIOD_H4, PERIOD_D1, PERIOD_W1, PERIOD_MN1 };
我们的指标OnInit()函数会调用一个函数来创建面板和面板内的对象。同时,它也会对用于计算趋势的K线进行快速检查,少于3根K线计算趋势基本没有意义。
//+------------------------------------------------------------------+ //| 自定义指标初始化函数 | //+------------------------------------------------------------------+ int OnInit() { // 创建指标面板的对象 if(Create_Panel() && BarCount >= 3) return(INIT_SUCCEEDED); else return(INIT_FAILED); }
关于GetTrend()函数的具体工作原理我不打算深入讨论,这里有许多优秀的文档可以参考。我们先来看一下图形显示趋势的DisplayTrend()函数。
//+------------------------------------------------------------------+ //| 显示所有时间框架的当前趋势 //+------------------------------------------------------------------+ void DisplayTrend(void) { int i, cntr, Trend, LastTrend; string str; for(i=1; i<10; i++) { str = "趋势" + DoubleToStr(i, 0); Trend = (int)GetTrend(TrendPeriods[i-1], BarCount, Method); if(Trend == FLATTREND) { // 如果趋势平稳,查找最后的趋势方向 cntr = 1; do { LastTrend = (int)GetTrend(TrendPeriods[i-1], BarCount, Method, false, cntr++); } while(LastTrend == Trend); ObjectSetText(str, TrendFlat, 8, "WingDings", (LastTrend == UPTREND ? Green : Red)); ObjectSetInteger(0, str, OBJPROP_YDISTANCE, 6); } else { ObjectSetText(str, (Trend == UPTREND ? TrendUp : TrendDown), 8, "WingDings", (Trend == UPTREND ? Green : Red)); ObjectSetInteger(0, str, OBJPROP_YDISTANCE,5 + (Trend == UPTREND ? 1 : - 1)); } } }
基本上,我们循环遍历TrendPeriods[]数组(以获取所有时间框架)并根据该时间框架的趋势方向设置趋势箭头。如果趋势是平坦的,我们会找到第一个非平坦的方向,以了解趋势的来源,并以该方向的颜色显示箭头。
在OnInit()中的Create_Panel()函数创建了锚定在屏幕左下角的对象,并使用TrendPosition[]将箭头放置在正确的位置。
//+------------------------------------------------------------------+ //| 在屏幕左下角创建趋势面板 //+------------------------------------------------------------------+ bool Create_Panel(void) { int i; string str; // 创建趋势指标窗口 if(ObjectCreate(TrendPanel, OBJ_RECTANGLE_LABEL, 0, 0, 0)) { ObjectSetInteger(0, TrendPanel, OBJPROP_XDISTANCE, 1); ObjectSetInteger(0, TrendPanel, OBJPROP_YDISTANCE, 29); ObjectSetInteger(0, TrendPanel, OBJPROP_XSIZE, 240); ObjectSetInteger(0, TrendPanel, OBJPROP_YSIZE, 26); ObjectSetInteger(0, TrendPanel, OBJPROP_BGCOLOR, White); ObjectSetInteger(0, TrendPanel, OBJPROP_BORDER_TYPE, 0); ObjectSetInteger(0, TrendPanel, OBJPROP_CORNER, CORNER_LEFT_LOWER); ObjectSetInteger(0, TrendPanel, OBJPROP_COLOR, Red); ObjectSetInteger(0, TrendPanel, OBJPROP_STYLE, STYLE_SOLID); ObjectSetInteger(0, TrendPanel, OBJPROP_WIDTH, 2); ObjectSetInteger(0, TrendPanel, OBJPROP_BACK, false); ObjectSetInteger(0, TrendPanel, OBJPROP_SELECTABLE,false); ObjectSetInteger(0, TrendPanel, OBJPROP_SELECTED, false); ObjectSetInteger(0, TrendPanel, OBJPROP_HIDDEN, true); ObjectSetString(0, TrendPanel, OBJPROP_TOOLTIP, "\n"); } else { PrintFormat(StatObjectError, __FUNCTION__, __LINE__, TrendPanel, GetLastError()); return(false); } if(ObjectCreate(InfoLine1, OBJ_LABEL, 0, 0, 0)) { ObjectSet(InfoLine1, OBJPROP_CORNER, CORNER_LEFT_LOWER); ObjectSet(InfoLine1, OBJPROP_XDISTANCE, 6); ObjectSet(InfoLine1, OBJPROP_YDISTANCE, 15); ObjectSetInteger( 0, InfoLine1, OBJPROP_SELECTABLE, false); ObjectSetInteger( 0, InfoLine1, OBJPROP_HIDDEN, true); ObjectSetString( 0, InfoLine1, OBJPROP_TOOLTIP, "\n"); ObjectSetText(InfoLine1, TrendLabel, 8, "Arial", Black); } else { PrintFormat(StatObjectError, __FUNCTION__, __LINE__, InfoLine1, GetLastError()); return( false); } if(ObjectCreate(InfoLine2, OBJ_LABEL, 0, 0, 0)) { ObjectSet(InfoLine2, OBJPROP_CORNER, CORNER_LEFT_LOWER); ObjectSet(InfoLine2, OBJPROP_XDISTANCE, 6); ObjectSet(InfoLine2, OBJPROP_YDISTANCE, 5); ObjectSetInteger( 0, InfoLine2, OBJPROP_SELECTABLE, false); ObjectSetInteger( 0, InfoLine2, OBJPROP_HIDDEN, true); ObjectSetString( 0, InfoLine2, OBJPROP_TOOLTIP, "\n"); ObjectSetText(InfoLine2, " " + DoubleToStr(BarCount, 0) + " / " + DoubleToStr(Method, 0), 8, "Arial", Black); } else { PrintFormat(StatObjectError, __FUNCTION__, __LINE__, InfoLine2, GetLastError()); return( false); } // 创建趋势对象并显示当前趋势 for(i=1; i<10; i++) { str = "趋势" + DoubleToStr(i, 0); if(ObjectCreate(str, OBJ_LABEL, 0, 0, 0)) { ObjectSet(str, OBJPROP_CORNER, CORNER_LEFT_LOWER); ObjectSet(str, OBJPROP_XDISTANCE, TrendPosition[i- 1]); ObjectSet(str, OBJPROP_YDISTANCE, 5); ObjectSetInteger( 0, str, OBJPROP_SELECTABLE, false); ObjectSetInteger( 0, str, OBJPROP_HIDDEN, true); ObjectSetString( 0, str, OBJPROP_TOOLTIP, "\n"); } else { PrintFormat(StatObjectError, __FUNCTION__, __LINE__, str, GetLastError()); return( false); } } // 显示当前趋势 DisplayTrend(); // 一切正常 return( true); }
这个函数如果成功创建所有对象,将返回TRUE,否则返回FALSE。值得一提的是,指标会在控制台的EXPERT标签中显示错误代码以及出错的代码行号。
我们还需要在每个新tick到达时更新趋势指标,这通过在start()函数中调用DisplayTrend()函数来实现。
//+------------------------------------------------------------------+ //| 自定义指标迭代函数 | //+------------------------------------------------------------------+ int start() { // 显示所有时间框架的当前趋势 DisplayTrend(); return(0); }
最后,当我们关闭指标时,需要销毁所有由指标创建的对象。
//+------------------------------------------------------------------+ //| 去初始化函数 | //+------------------------------------------------------------------+ void OnDeinit(const int reason) { // 删除指标的对象 if(ObjectFind(TrendPanel) >=0) ObjectDelete(TrendPanel); if(ObjectFind(InfoLine1) >= 0) ObjectDelete(InfoLine1); if(ObjectFind(InfoLine2) >= 0) ObjectDelete(InfoLine2); for(int i=1; i< 10; i++) if(ObjectFind("趋势"+ DoubleToStr(i, 0)) >= 0) ObjectDelete( "趋势"+ DoubleToStr(i, 0)); }
就是这样。现在你有一个实用的指标面板,可以显示所有时间框架的当前趋势,位置在屏幕的左下角。
希望你们喜欢这个指标!