
构建K线图趋势约束模型(第三部分):在使用该系统时检测趋势变化
内容
- 引言
- 识别市场趋势变化的方法
- 使用移动平均线来识别趋势反转
- 使用K线图模式来识别趋势反转
- 使用趋势线来识别趋势反转
- 使用支撑阻力线来识别趋势反转
- 我们当前系统存在的问题
- 使用MQL5将新功能融入我们的程序中
- 研究最终系统的结果
- 视频结果说明
- 结论
引言
通常,市场不会保持静止。无论市场是上升趋势还是下降趋势,当市场改变其轨迹时,都可能出现意外的波动。系统识别和适应这些变化至关重要。即使是一个长时间框架的日线阴线,在较短的时间框架内发生反转时,也可能预示着市场趋势的动态变化。本文深入探讨了识别价格行为趋势变化的各种方法。当交易者身处金融市场的复杂环境中时,迅速适应不断变化的环境的能力变得至关重要。识别价格波动的细微差别、理解关键指标的重要性以及解读市场情绪,都是交易者在市场中保持优势的重要组成部分。通过磨练识别趋势变化的能力,交易者可以在金融世界不断变化的格局中,战略性地定位自己,以抓住出现的机遇。
多种因素会影响或驱动市场趋势的变化。以下是几个例子:
- 投资者行为,即买卖活动
- 经济新闻发布,如国内生产总值(GDP)和非农就业人口数据
- 货币政策
- 全球事件,如自然灾害
- 政治事件,如战争等。
通过各种学习资源,我们已经掌握了大量关于如何手动检测趋势变化的知识。这包括趋势线分析的概念,它涉及在价格图表上划线以连接资产价格走势的高点或低点,这样当价格突破这些线时,交易者就能洞察到潜在的趋势变化。在本文的后续部分,我们将选择一种方法来检测市场趋势的变化,并使用MQL5将其整合到我们的趋势约束指标中。首先,我们将探索各种技术分析工具,如移动平均线、K线图模式、相对强弱指数(参见第二部分)和趋势线(见图1),以识别潜在的趋势反转。接下来,我们将修改我们的MQL5趋势约束指标,以纳入这一新功能。
图1:趋势图示
上述图示描绘了一个典型的上升趋势,低点A和B由一条蓝色趋势线连接,表明了一个上升趋势。在以下示例中,我们将研究真实的图表实例,以进一步理解趋势。我们的主要目标是识别市场趋势的变化,并使用MQL5代码将合适的方法整合到我们的系统中。
检测市场趋势变化的方法。
让我们首先定义市场趋势为:
市场趋势表示市场随时间推移的总体移动方向,反映了买家和卖家的行为。趋势可以是上升趋势(牛市)、下降趋势(熊市)或横向趋势(盘整),见图1(在引言中)。
现在让我们定义趋势反转为:
趋势反转发生在价格走势从上升趋势转变为下降趋势,或反之亦然时。通过分析关键的技术指标,如移动平均线、趋势线、蜡烛图模式和支撑/阻力位,可以识别出这种转变。交易者和投资者密切关注市场趋势的这些变化,以便做出明智的决策并相应地调整他们的策略。
使用移动平均线来识别趋势反转
在本系列文章的第一部分中,我们创建了一个快速移动平均线交叉指标,用于在交叉发生时提供趋势持续信号。然而,较长周期的移动平均线的交叉指示出显著的趋势变化。在本文中,我们将研究当市场改变方向时,移动平均线会如何反应。下面是一张图片
图2:移动平均线交叉作为趋势反转信号
使用K线图形态来识别趋势反转
K线图(又称蜡烛图)形态以被有效地用来识别可能的反转信号。它们历来因其能够显著改变市场情绪的能力而备受分析。像本间宗久(Honma Munehisa)这样的先驱者,作为《蜡烛图圣经》的创作者,极大地丰富了我们对蜡烛图的理解。以下是市场反转时最常见的一些蜡烛图形态:
蜡烛图名称 | 说明 |
---|---|
锤头线 | 小实体和长的下影线 |
倒锤头线 | 小实体和长上影线 |
看涨吞没形态 | 一根看涨蜡烛线完全吞没了前一根看跌蜡烛线 |
看跌吞没形态 | 一根看跌蜡烛线完全吞没了前一根看涨蜡烛线 |
十字星 | 小实体和长上影线及长下影线 |
射击之星 | 小实体和长上影线 |
上吊线 | 小实体和长的下影线 |
启明星 | 一根长长的看跌蜡烛线之后,跟着一根小实体的蜡烛线,该蜡烛线的最低价低于前一根蜡烛线的最低价,而最高价则高于前一根蜡烛线的最高价 |
黄昏之星 | 一根长长的看涨蜡烛线之后,跟着一根小实体的蜡烛线,该蜡烛线的最高价高于前一根蜡烛线的最高价,而最低价则低于前一根蜡烛线的最低价 |
使用趋势线来识别趋势反转
在Mt5平台的图表上,我们可以使用趋势线对象工具,通过连接数字资产价格序列中的连续低点来绘制趋势。趋势线的断裂表明趋势发生了变化。要绘制趋势线,只需在工具栏中点击趋势线工具,然后点击第一个低点,并将线条拖动到下一个低点。趋势线会自动延伸到图表的右侧。
图3:趋势线作为检测趋势反转的工具
使用支撑阻力位来识别趋势反转
在MT5图表中,可以使用水平线工具通过标注价格峰值来绘制趋势中的支撑位和阻力位。当价格突破这些位置时,可能意味着趋势发生了变化。请查看下面的视频讲解。
我们当前系统存在的问题。
我们已经成功配置了系统,使其信号与D1趋势形态保持一致,并在第二部分中加入了如SMA 400等指标。然而,在历史数据中我们观察到了显著的问题。我们必须考虑较短时间框架内的波动,这些波动可能会在一天开始时逆转最初的市场情绪;例如,一天可能以看跌态势开始,但最终以针状图或看涨态势结束。反转信号经常在较短的时间框架中出现,这促使我们需要调整系统的信号机制,以考虑趋势变化,即使这些变化最初仅限于每日市场情绪。
将趋势变化检测功能融入MQL5
您已经选择了使用SMA 200作为慢速移动平均线,EMA 100作为快速移动平均线。通常,在强趋势期间,这两条移动平均线之间的距离会较远,而在市场动量较弱或价格横向移动时,它们会靠得更近。当这两条平均线交叉时,通常意味着方向发生了变化。如果我们的系统能够检测到这一点并向我们发出警报,那将非常有用。我们的目标是让系统仅向我们提供信号,以便我们可以相应地调整我们的约束信号。采用这种方法,我们旨在捕捉潜在的趋势反转并利用有利可图的交易机会。接下来,我将解释一些关键功能,然后附上主要代码。
我们的移动平均线(MA)处理程序声明如下。
MA_handle3 = iMA(NULL, PERIOD_CURRENT, 100, 0, MODE_EMA, PRICE_CLOSE); // For EMA 100 MA_handle4 = iMA(NULL, PERIOD_CURRENT, 200, 0, MODE_SMA, PRICE_CLOSE); // For SMA 200
下面的代码展示了在MQL5的迭代函数中产生的均线交叉条件。
//Indicator Buffer 3 if(MA3[i] > MA4[i] && MA3[i+1] < MA4[i+1] //Moving Average crosses above Moving Average ) { Buffer3[i] = Low[i]; //Set indicator value at Candlestick Low if(i == 1 && Time[1] != time_alert) myAlert("indicator", "Buy Reversal"); //Alert on next bar open time_alert = Time[1]; } else { Buffer3[i] = EMPTY_VALUE; } //Indicator Buffer 4 if(MA3[i] < MA4[i] && MA3[i+1] > MA4[i+1] //Moving Average crosses below Moving Average ) { Buffer4[i] = High[i]; //Set indicator value at Candlestick High if(i == 1 && Time[1] != time_alert) myAlert("indicator", "Sell Reversal"); //Alert on next bar open time_alert = Time[1]; } else { Buffer4[i] = EMPTY_VALUE; }
在前一篇文章中提到的趋势约束功能包含两个缓冲区,一个用于表示趋势延续的买入信号,另一个用于表示卖出信号。为了实现我们的目标,我们想要再增加两个缓冲区,一个用于卖出信号,另一个用于买入信号,这两个信号都代表在交叉发生时出现的反转信号。在程序中,这两个新的缓冲区被连续命名为Buffer3和Buffer4。我们为这个功能优化了新的显示样式。指标的显示样式可以通过从mql5对象库中选择Wingdings字体来自定义。在这里,我使用了对象编号236来表示买入反转信号,使用了对象编号238来表示卖出反转信号。
// under OnInit() function. The wingding objects can be customized by altering those highlighted values choosing from wingding listing. SetIndexBuffer(2, Buffer3); PlotIndexSetDouble(2, PLOT_EMPTY_VALUE, EMPTY_VALUE); PlotIndexSetInteger(2, PLOT_DRAW_BEGIN, MathMax(Bars(Symbol(), PERIOD_CURRENT)-PLOT_MAXIMUM_BARS_BACK+1, OMIT_OLDEST_BARS+1)); PlotIndexSetInteger(2, PLOT_ARROW, 236); SetIndexBuffer(3, Buffer4); PlotIndexSetDouble(3, PLOT_EMPTY_VALUE, EMPTY_VALUE); PlotIndexSetInteger(3, PLOT_DRAW_BEGIN, MathMax(Bars(Symbol(), PERIOD_CURRENT)-PLOT_MAXIMUM_BARS_BACK+1, OMIT_OLDEST_BARS+1)); PlotIndexSetInteger(3, PLOT_ARROW, 238);
另一部分是使用MQL5控制颜色的代码。这些颜色可以通过MetaTrader 5的输入设置进行优化。在程序中,每种颜色都由一个唯一的代码来表示。例如:"C'0,0,0'" 代表黑色,具体代码片段如下:
#property indicator_type3 DRAW_ARROW #property indicator_width3 1 // with can be adjusted up to 5 times. #property indicator_color3 0x04CC04 //color for buy reversal #property indicator_label3 "buy reversal" #property indicator_type4 DRAW_ARROW #property indicator_width4 1 //with can be adjusted up to 5 times. #property indicator_color4 0xE81AC6 // Color code for sell reversal #property indicator_label4 "sell reversal"
以下是将所有部分和想法结合在一起的主代码,其中包含更多细节和注释:
///Indicator Name: Trend Constraint #property copyright "Clemence Benjamin" #property link "https://mql5.com" #property version "1.03" #property description "A model that seek to produce sell signal when D1 candle is Bearish only and buy signal when it is Bullish" //+------------------------------------------------------------------------------------------------------------------------------+ //--- indicator settings #property indicator_chart_window #property indicator_buffers 4 #property indicator_plots 4 #property indicator_type1 DRAW_ARROW #property indicator_width1 5 #property indicator_color1 0xFF3C00 #property indicator_label1 "Buy" #property indicator_type2 DRAW_ARROW #property indicator_width2 5 #property indicator_color2 0x0000FF #property indicator_label2 "Sell" #property indicator_type3 DRAW_ARROW #property indicator_width3 1 #property indicator_color3 0x04CC04 #property indicator_label3 "Buy Reversal" #property indicator_type4 DRAW_ARROW #property indicator_width4 1 #property indicator_color4 0xE81AC6 #property indicator_label4 "Sell Reversal" #define PLOT_MAXIMUM_BARS_BACK 5000 #define OMIT_OLDEST_BARS 50 //--- indicator buffers double Buffer1[]; double Buffer2[]; double Buffer3[]; double Buffer4[]; input double Oversold = 30; input double Overbought = 70; datetime time_alert; //used when sending alert input bool Audible_Alerts = true; input bool Push_Notifications = true; double myPoint; //initialized in OnInit int RSI_handle; double RSI[]; double Open[]; double Close[]; int MA_handle; double MA[]; int MA_handle2; double MA2[]; int MA_handle3; double MA3[]; int MA_handle4; double MA4[]; double Low[]; double High[]; void myAlert(string type, string message) { if(type == "print") Print(message); else if(type == "error") { Print(type+" | Trend Constraint V1.03 @ "+Symbol()+","+IntegerToString(Period())+" | "+message); } else if(type == "order") { } else if(type == "modify") { } else if(type == "indicator") { if(Audible_Alerts) Alert(type+" | Trend Constraint V1.03 @ "+Symbol()+","+IntegerToString(Period())+" | "+message); if(Push_Notifications) SendNotification(type+" | Trend Constraint V1.03 @ "+Symbol()+","+IntegerToString(Period())+" | "+message); } } //+------------------------------------------------------------------+ //| Custom indicator initialization function | //+------------------------------------------------------------------+ int OnInit() { SetIndexBuffer(0, Buffer1); PlotIndexSetDouble(0, PLOT_EMPTY_VALUE, EMPTY_VALUE); PlotIndexSetInteger(0, PLOT_DRAW_BEGIN, MathMax(Bars(Symbol(), PERIOD_CURRENT)-PLOT_MAXIMUM_BARS_BACK+1, OMIT_OLDEST_BARS+1)); PlotIndexSetInteger(0, PLOT_ARROW, 241); SetIndexBuffer(1, Buffer2); PlotIndexSetDouble(1, PLOT_EMPTY_VALUE, EMPTY_VALUE); PlotIndexSetInteger(1, PLOT_DRAW_BEGIN, MathMax(Bars(Symbol(), PERIOD_CURRENT)-PLOT_MAXIMUM_BARS_BACK+1, OMIT_OLDEST_BARS+1)); PlotIndexSetInteger(1, PLOT_ARROW, 242); SetIndexBuffer(2, Buffer3); PlotIndexSetDouble(2, PLOT_EMPTY_VALUE, EMPTY_VALUE); PlotIndexSetInteger(2, PLOT_DRAW_BEGIN, MathMax(Bars(Symbol(), PERIOD_CURRENT)-PLOT_MAXIMUM_BARS_BACK+1, OMIT_OLDEST_BARS+1)); PlotIndexSetInteger(2, PLOT_ARROW, 236); SetIndexBuffer(3, Buffer4); PlotIndexSetDouble(3, PLOT_EMPTY_VALUE, EMPTY_VALUE); PlotIndexSetInteger(3, PLOT_DRAW_BEGIN, MathMax(Bars(Symbol(), PERIOD_CURRENT)-PLOT_MAXIMUM_BARS_BACK+1, OMIT_OLDEST_BARS+1)); PlotIndexSetInteger(3, PLOT_ARROW, 238); //initialize myPoint myPoint = Point(); if(Digits() == 5 || Digits() == 3) { myPoint *= 10; } RSI_handle = iRSI(NULL, PERIOD_CURRENT, 14, PRICE_CLOSE); if(RSI_handle < 0) { Print("The creation of iRSI has failed: RSI_handle=", INVALID_HANDLE); Print("Runtime error = ", GetLastError()); return(INIT_FAILED); } MA_handle = iMA(NULL, PERIOD_CURRENT, 7, 0, MODE_SMMA, PRICE_CLOSE); if(MA_handle < 0) { Print("The creation of iMA has failed: MA_handle=", INVALID_HANDLE); Print("Runtime error = ", GetLastError()); return(INIT_FAILED); } MA_handle2 = iMA(NULL, PERIOD_CURRENT, 400, 0, MODE_SMA, PRICE_CLOSE); if(MA_handle2 < 0) { Print("The creation of iMA has failed: MA_handle2=", INVALID_HANDLE); Print("Runtime error = ", GetLastError()); return(INIT_FAILED); } MA_handle3 = iMA(NULL, PERIOD_CURRENT, 100, 0, MODE_EMA, PRICE_CLOSE); if(MA_handle3 < 0) { Print("The creation of iMA has failed: MA_handle3=", INVALID_HANDLE); Print("Runtime error = ", GetLastError()); return(INIT_FAILED); } MA_handle4 = iMA(NULL, PERIOD_CURRENT, 200, 0, MODE_SMA, PRICE_CLOSE); if(MA_handle4 < 0) { Print("The creation of iMA has failed: MA_handle4=", INVALID_HANDLE); Print("Runtime error = ", GetLastError()); return(INIT_FAILED); } return(INIT_SUCCEEDED); } //+------------------------------------------------------------------+ //| Custom indicator iteration function | //+------------------------------------------------------------------+ int OnCalculate(const int rates_total, const int prev_calculated, const datetime& time[], const double& open[], const double& high[], const double& low[], const double& close[], const long& tick_volume[], const long& volume[], const int& spread[]) { int limit = rates_total - prev_calculated; //--- counting from 0 to rates_total ArraySetAsSeries(Buffer1, true); ArraySetAsSeries(Buffer2, true); ArraySetAsSeries(Buffer3, true); ArraySetAsSeries(Buffer4, true); //--- initial zero if(prev_calculated < 1) { ArrayInitialize(Buffer1, EMPTY_VALUE); ArrayInitialize(Buffer2, EMPTY_VALUE); ArrayInitialize(Buffer3, EMPTY_VALUE); ArrayInitialize(Buffer4, EMPTY_VALUE); } else limit++; datetime Time[]; datetime TimeShift[]; if(CopyTime(Symbol(), PERIOD_CURRENT, 0, rates_total, TimeShift) <= 0) return(rates_total); ArraySetAsSeries(TimeShift, true); int barshift_M1[]; ArrayResize(barshift_M1, rates_total); int barshift_D1[]; ArrayResize(barshift_D1, rates_total); for(int i = 0; i < rates_total; i++) { barshift_M1[i] = iBarShift(Symbol(), PERIOD_M1, TimeShift[i]); barshift_D1[i] = iBarShift(Symbol(), PERIOD_D1, TimeShift[i]); } if(BarsCalculated(RSI_handle) <= 0) return(0); if(CopyBuffer(RSI_handle, 0, 0, rates_total, RSI) <= 0) return(rates_total); ArraySetAsSeries(RSI, true); if(CopyOpen(Symbol(), PERIOD_M1, 0, rates_total, Open) <= 0) return(rates_total); ArraySetAsSeries(Open, true); if(CopyClose(Symbol(), PERIOD_D1, 0, rates_total, Close) <= 0) return(rates_total); ArraySetAsSeries(Close, true); if(BarsCalculated(MA_handle) <= 0) return(0); if(CopyBuffer(MA_handle, 0, 0, rates_total, MA) <= 0) return(rates_total); ArraySetAsSeries(MA, true); if(BarsCalculated(MA_handle2) <= 0) return(0); if(CopyBuffer(MA_handle2, 0, 0, rates_total, MA2) <= 0) return(rates_total); ArraySetAsSeries(MA2, true); if(BarsCalculated(MA_handle3) <= 0) return(0); if(CopyBuffer(MA_handle3, 0, 0, rates_total, MA3) <= 0) return(rates_total); ArraySetAsSeries(MA3, true); if(BarsCalculated(MA_handle4) <= 0) return(0); if(CopyBuffer(MA_handle4, 0, 0, rates_total, MA4) <= 0) return(rates_total); ArraySetAsSeries(MA4, true); if(CopyLow(Symbol(), PERIOD_CURRENT, 0, rates_total, Low) <= 0) return(rates_total); ArraySetAsSeries(Low, true); if(CopyHigh(Symbol(), PERIOD_CURRENT, 0, rates_total, High) <= 0) return(rates_total); ArraySetAsSeries(High, true); if(CopyTime(Symbol(), Period(), 0, rates_total, Time) <= 0) return(rates_total); ArraySetAsSeries(Time, true); //--- main loop for(int i = limit-1; i >= 0; i--) { if (i >= MathMin(PLOT_MAXIMUM_BARS_BACK-1, rates_total-1-OMIT_OLDEST_BARS)) continue; //omit some old rates to prevent "Array out of range" or slow calculation if(barshift_M1[i] < 0 || barshift_M1[i] >= rates_total) continue; if(barshift_D1[i] < 0 || barshift_D1[i] >= rates_total) continue; //Indicator Buffer 1 if(RSI[i] < Oversold && RSI[i+1] > Oversold //Relative Strength Index crosses below fixed value && Open[barshift_M1[i]] >= Close[1+barshift_D1[i]] //Candlestick Open >= Candlestick Close && MA[i] > MA2[i] //Moving Average > Moving Average && MA3[i] > MA4[i] //Moving Average > Moving Average ) { Buffer1[i] = Low[i]; //Set indicator value at Candlestick Low if(i == 1 && Time[1] != time_alert) myAlert("indicator", "Buy"); //Alert on next bar open time_alert = Time[1]; } else { Buffer1[i] = EMPTY_VALUE; } //Indicator Buffer 2 if(RSI[i] > Overbought && RSI[i+1] < Overbought //Relative Strength Index crosses above fixed value && Open[barshift_M1[i]] <= Close[1+barshift_D1[i]] //Candlestick Open <= Candlestick Close && MA[i] < MA2[i] //Moving Average < Moving Average && MA3[i] < MA4[i] //Moving Average < Moving Average ) { Buffer2[i] = High[i]; //Set indicator value at Candlestick High if(i == 1 && Time[1] != time_alert) myAlert("indicator", "Sell"); //Alert on next bar open time_alert = Time[1]; } else { Buffer2[i] = EMPTY_VALUE; } //Indicator Buffer 3 if(MA3[i] > MA4[i] && MA3[i+1] < MA4[i+1] //Moving Average crosses above Moving Average ) { Buffer3[i] = Low[i]; //Set indicator value at Candlestick Low if(i == 1 && Time[1] != time_alert) myAlert("indicator", "Buy Reversal"); //Alert on next bar open time_alert = Time[1]; } else { Buffer3[i] = EMPTY_VALUE; } //Indicator Buffer 4 if(MA3[i] < MA4[i] && MA3[i+1] > MA4[i+1] //Moving Average crosses below Moving Average ) { Buffer4[i] = High[i]; //Set indicator value at Candlestick High if(i == 1 && Time[1] != time_alert) myAlert("indicator", "Sell Reversal"); //Alert on next bar open time_alert = Time[1]; } else { Buffer4[i] = EMPTY_VALUE; } } return(rates_total); } //Thank you for getting this far, you are amazing. //+---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------+
研究最终系统的结果
系统给出的结果令人印象深刻。我们现在可以将信号系统限制在日线(D1)的市场情绪上,并在EMA(100)和SMA(200)交叉时接收趋势反转信号。以下是指标在实际历史图表上的输出图像。该系统在捕捉市场情绪变化和识别潜在反转点方面表现得尤为出色。通过关注日线(D1)市场情绪并利用EMA(100)和SMA(200)交叉信号,我们能够改进交易策略并做出更明智的决策。指标在历史图表数据上的输出清晰地展示了这些信号在预测市场走势方面的有效性。
图4:Trend Constraint V1.03在USDJPYmicroM1上的结果
注意:如果在添加指标后图表上缺少信号箭头,请尝试在MT5图表上右键单击鼠标,并从出现的菜单中选择“刷新”。
从我们的程序中获取的数据结果可以在后续被整合到机器学习和人工智能系统中,以进行进一步的优化。这些系统可以经过训练来执行高级分析,这将有助于克服我们当前模型所面临的挑战。上述结果图像中的信号与我们的想法相符,但也存在一些误导性信号。这对于任何系统来说都是典型的,并激励我们去探索改进当前系统的其他方法。在使用此系统时,合成的数据可能会带来不同的结果。
视频结果说明
观看下面的视频来研究我们开发的新版程序的性能。
结论
将趋势变化检测功能整合到我们的系统中,已显著提升了其性能。尽管我们将信号限制在当前市场趋势上,但我们已成功减少了因支持无效趋势(尽管日线的市场情绪支持它)而产生的潜在损失。在趋势持续期间,我们遇到了该系统提供的存在问题的反转信号。为了定位这一问题,我决定延长所使用的移动平均线的周期。在后续文章中,我将重新探讨这个话题,并深入探讨这一调整是如何展开的。我相信您从这次讨论中获得了有价值的信息,并欢迎在下方的评论部分发表您的看法。我们未来的文章将使用功能强大的MQL5语言,对我们的指标系统进行高级可视化展示。
本文由MetaQuotes Ltd译自英文
原文地址: https://www.mql5.com/en/articles/14853
注意: MetaQuotes Ltd.将保留所有关于这些材料的权利。全部或部分复制或者转载这些材料将被禁止。
This article was written by a user of the site and reflects their personal views. MetaQuotes Ltd is not responsible for the accuracy of the information presented, nor for any consequences resulting from the use of the solutions, strategies or recommendations described.


