
重新审视一种旧时的趋势交易策略:两个随机振荡指标,一个移动平均指标和斐波那契线
简介策略细节
该策略纯粹是技术性的,使用一些技术指标和工具来传递信号和目标。该策略的组成部分如下:
- 一个周期数为14的随机振荡指标,
- 一个周期数为5的随机振荡指标,
- 一个周期数为200的移动平均指标,
- 一个斐波那契投影工具(用于设定目标)。
随机振荡指标是一种流行的技术分析工具,交易员和投资者使用它来衡量金融工具价格波动的动量和强度。它是由 George C. Lane 在1950年代开发的。它的范围在0和100之间,接近下限的值被称为超卖水平(看涨偏差),接近上限的值被视为超买水平(看跌偏差)。
每日时段下周期数为5的随机振荡指标定义
如下:
其中,High 和 Low 分别是最近5天内的最高和最低价格,而%D是%K的N天移动平均值(%K的最后N个值)。通常这是一个简单的移动平均值,但可以是最近值的不太标准化加权的指数移动平均值。单独使用%D时只有一个有效信号 — %D和所分析资产之间的背离。
移动平均线是一种常用的统计计算方法,有助于平滑特定时间段内的价格数据。它广泛应用于金融、经济和信号处理等各个领域。在金融市场的背景下,移动平均线经常被用于技术分析,以确定趋势并产生交易信号。
移动平均值是通过取给定时间段内一组价格的平均值,并在新数据可用时进行更新来计算的。随着每个新数据点的添加,最旧的数据点会被删除,从而产生反映最新价格的“移动”平均值。
实现策略
该策略的交易规则如下:
- 每当两个随机振荡指标同时达到超卖水平,反弹,然后(大约在同一时间)回到超卖水平时,就会产生买入信号。整个过程必须在市场价格高于200周期移动平均线时完成。第一个目标是使用斐波那契投影工具设置的,该工具应用于随机振荡指标第一次到达其底部的低点和第二次到达底部的低点之间。因此,第一个目标是61.8%的投影,第二个目标是100.0%的投影。
- 每当两个随机振荡指标同时达到超买水平,反弹,然后(大约在同一时间)回到超买水平时,就会产生一个卖出信号。整个过程必须在市场价格低于200周期移动平均线时完成。第一个目标是使用斐波那契投影工具设置的,该工具应用于随机振荡指标第一次达到其顶部时的高点和第二次达到其峰值时的高点之间。因此,第一个目标是61.8%的投影,第二个目标是100.0%的投影。
(我已经对策略进行了更改,在每个斐波那契水平中都有止损水平)
下图显示了看跌信号:
最终,结果可能因市场而异,目前的结果可能并不稳定。策略在某些时期有效,但在其他时期可能表现不佳。
该策略的止损是目标的一半,从而实现2.0的风险回报率,也可以是每当市场突破移动平均线时触发止损。
在金融领域,斐波那契回扫是一种技术分析方法,用于确定支撑位和阻力位。它是以斐波那契数列命名的,其比率提供了价格水平,在趋势继续朝着原来的方向发展之前,市场倾向于将波动的一部分回调。
斐波那契回扫预测是通过在图表上取两个极值点并将垂直距离除以斐波那奇比率来创建的。0%被认为是回调的开始,而100%则是对移动前的原始价格的完全反转。图表中画出了这些价格水平的水平线(不在此EA中),以提供支撑和阻力水平。
常见水平分别为23.6%、38.2%、50%和61.8%。
结果
尽管这是一种有利可图的策略,但市场不同,目前的结果可能并不稳定。策略在某些时期有效,但在其他时期可能表现不佳。请在使用前测试策略,并修改值以达到最佳效果,如偏移天数或时间段。
我们可以使用它来转化所有数据。
int Highest = iHighest(Symbol(),my_timeframe,MODE_CLOSE,shift,1); double High0=iHigh(Symbol(),my_timeframe,0); int Lowest = iLowest(Symbol(),my_timeframe,MODE_CLOSE,shift,1); double Low0=iLow(Symbol(),my_timeframe,0);
但出于测试原因,我们将使用较小的偏移值(例如300)。
这是2013年初至2023年(6月20日)shift=300、timeperiod=30分钟和2%risk的结果。
在金融领域,夏普比率(也称为夏普指数或回报与可变性比率)衡量的是在对风险进行调整后,与无风险资产相比,证券或投资组合等投资的表现。它被定义为投资回报与无风险回报之间的差额,除以投资回报的标准差。它代表投资者每增加一个风险单位所获得的额外回报。
代码
在 OnInit 函数中我们将会声明我们将要使用的技术指标句柄:
void OnInit() { Stochastic_handle1 = iStochastic(_Symbol, PERIOD_CURRENT, Signal_0_Stoch_PeriodK, Signal_0_Stoch_PeriodD, Signal_0_Stoch_PeriodSlow, MODE_SMA, STO_LOWHIGH); Stochastic_handle2 = iStochastic(_Symbol, PERIOD_CURRENT, Signal_1_Stoch_PeriodK, Signal_1_Stoch_PeriodD, Signal_1_Stoch_PeriodSlow, MODE_SMA, STO_LOWHIGH); //--- create handle of the indicator iMA handle_iMA=iMA(_Symbol,PERIOD_CURRENT,Inp_MA_ma_period,Inp_MA_ma_shift, Inp_MA_ma_method,Inp_MA_applied_price); //Test Alert("Expert Advisor has been launched"); }
此代码为随机振荡指标创建一个句柄,句柄名称为 Stochastic_handle1。iStochastic() 函数就是用于创建句柄的,传递给函数的参数是交易品种、当前周期、%K线的周期、%D线的周期和慢线的周期,慢线的模式以及随机(低价/高价)的类型。
此代码用于计算具有指定参数的当前交易品种的移动平均值(MA)。handle_iMA 变量保存了MA计算的结果。计算中使用的参数是交易品种、当前周期、MA周期、MA偏移、MA方法和应用价格类型。交易品种就是用于计算MA的货币对或者资产名称。当前周期是指计算MA时使用图表的时段。MA周期是指用于计算MA所使用的柱数。MA偏移是指计算MA时所要偏移的柱数。MA方法是指计算MA的类型,例如简单、指数、平滑或者线性加权。应用价格类型是指在计算MA时使用价格的类型,例如收盘价、开盘价、最高价、最低价、中间价、典型价格或者加权价格。
我们必须输入所有在OnInit之前在外部声明的参数。
在OnTick上,我们将使用它来获取每个分时的值。
MqlTick tick; SymbolInfoTick(_Symbol,tick);
此代码用于取得给定交易品种的分时数据。第一行声明了一个名为 tick 的 MqlTick 变量。第二行调用SymbolInfoTick()函数,该函数接受两个参数,即交易品种名称和tick变量。函数读取给定交易品种的分时数据,并将其存储在 tick 变量中。
为了模拟斐波那契回调水平,我们将使用以下方法:
int Highest = iHighest(Symbol(),my_timeframe,MODE_CLOSE,shift,1); double High0=iHigh(Symbol(),my_timeframe,0); int Lowest = iLowest(Symbol(),my_timeframe,MODE_CLOSE,shift,1); double Low0=iLow(Symbol(),my_timeframe,0); double highestValue = iHigh(Symbol(),my_timeframe,Highest); double lowestValue = iLow(Symbol(),my_timeframe,Lowest); // Obtener el valor más alto y más bajo de la barra actual double currentHigh = High0; double currentLow = Low0; // Obtener el valor más alto y más bajo de la barra anterior double previousHigh = highestValue; double previousLow = lowestValue; double level0s = currentHigh; double level1s = currentHigh - (currentHigh - previousLow) * 0.236; double level2s = currentHigh - (currentHigh - previousLow) * 0.382; double level3s = currentHigh - (currentHigh - previousLow) * 0.618; double level4s = previousLow; double level0b = currentLow; double level1b = currentLow + (-currentLow + previousHigh) * 0.236; double level2b = currentLow + (-currentLow + previousHigh) * 0.382; double level3b = currentLow + (-currentLow + previousHigh) * 0.618; double level4b = previousHigh;
MQL5语言中的此代码用于确定给定交易品种和周期数的最高价和最低价。前两行代码将交易品种和周期的最高价分配给变量 Highest,并将给shift=0处的交易品种和周期数最高价分配给与变量High0(当前烛形)。接下来的两行代码将交易品种和周期数的最低价分配给变量 Lowest,并将shift=0处的交易品种和周期数最低价分配给与变量Low0。
我们将使用一个偏移量,来选择我们将回顾的烛形数量。
对于MA我们将会使用这个:
//--- double array_ma[]; ArraySetAsSeries(array_ma,true); int start_pos=0,count=3; if(!iGetArray(handle_iMA,0,start_pos,count,array_ma)) return;
此代码用于从句柄中读取移动平均值的数组。第一行声明一个名为array_ma的数组,该数组将用于存储移动平均值。第二行将数组设置为一个序列。第三行和第四行声明了两个整数变量start_pos和count,这两个变量将用于指定起始位置和从句柄读取的元素数。第五行使用 iGetArray 函数从句柄中读取移动平均值,并将其存储在array_ma中。如果读取失败,函数会返回false。
使用这段(在 OnInit 之外)
bool iGetArray(const int handle,const int buffer,const int start_pos, const int count,double &arr_buffer[]) { bool result=true; if(!ArrayIsDynamic(arr_buffer)) { //if(InpPrintLog) PrintFormat("ERROR! EA: %s, FUNCTION: %s, this a no dynamic array!",__FILE__,__FUNCTION__); return(false); } ArrayFree(arr_buffer); //--- reset error code ResetLastError(); //--- fill a part of the iBands array with values from the indicator buffer int copied=CopyBuffer(handle,buffer,start_pos,count,arr_buffer); if(copied!=count) { //--- if the copying fails, tell the error code //if(InpPrintLog) PrintFormat("ERROR! EA: %s, FUNCTION: %s, amount to copy: %d, copied: %d, error code %d", __FILE__,__FUNCTION__,count,copied,GetLastError()); //--- quit with zero result - it means that the indicator is considered as not calculated return(false); } return(result); }
此代码用于将指标缓冲区的一部分复制到数组中。它接受五个参数,一个句柄、一个缓冲区、一个起始位置、一个计数和一个数组缓冲区。它首先检查数组缓冲区是否是动态的,如果不是,则打印一条错误消息。然后它重置错误代码,并将缓冲区复制到数组缓冲区中。如果复制的数量不等于计数,则会打印一条错误消息并返回false,否则,就返回true。
为了打印错误,并且为了在图表上显示结果,我们将使用以下方法:
string text=""; for(int i=0; i<count; i++) text=text+IntegerToString(i)+": "+DoubleToString(array_ma[i],Digits()+1)+"\n"; //--- Comment(text);
此代码用于打印MQL5语言中double类型的数组的值。代码首先声明一个名为“text”的字符串变量,并将其设置为空字符串。然后,使用for循环遍历数组,变量“i”用作循环计数器。对于每次迭代,“i”的值都会使用IntegerToString()函数转换为字符串,该索引处的数组的值会使用DoubleToString()函数。Digits()函数用于确定将双精度值转换为字符串时要使用的小数位数。然后将转换后的值连接到“text”字符串,并在每个值后面添加一个换行符。最后,Comment()函数用于打印出“text”字符串。
我们将对两个随机振荡指标采取同样的做法。
我们将设置条件,不允许同时打开多个订单,我们将使用此代码:
int total = PositionsTotal(); if(total==0) { ...code } if(total>0) { ...code }
为了下订单,我们将使用这样的代码:
if(!trade.Buy(get_lot(tick.bid),_Symbol,tick.bid,newStopLossLevelb1,newTakeProfitLevelb2)) { //--- failure message Print("Buy() method failed. Return code=",trade.ResultRetcode(), ". Code description: ",trade.ResultRetcodeDescription()); } else { Print("Buy() method executed successfully. Return code=",trade.ResultRetcode(), " (",trade.ResultRetcodeDescription(),")"); }
我们将同时使用 tick.bid,SL(止损) 和 TP(获利)。TP将是偏移周期内的最高价,而SL将是实际烛形中的最低值。
要在价格与MA交叉时关闭订单,我们将使用以下方法:
if(rates[1].close >array_ma22[0]) { trade.PositionClose(_Symbol,5); Print("cerro"); return; } else return; }
要修改TP和SL,我们将使用以下方法:
//--- setting the operation parameters request.action =TRADE_ACTION_SLTP; // type of trade operation request.position=position_ticket; // ticket of the position request.symbol=position_symbol; // symbol request.sl =newStopLossLevelss; // Stop Loss of the position request.tp =newTakeProfitLevelss; // Take Profit of the position request.magic=Expert_MagicNumber; // MagicNumber of the position //--- output information about the modification PrintFormat("Modify #%I64d %s %s",position_ticket,position_symbol,EnumToString(type)); //--- send the request if(!OrderSend(request,result)) PrintFormat("OrderSend error %d",GetLastError()); // if unable to send the request, output the error code //--- information about the operation PrintFormat("retcode=%u deal=%I64u order=%I64u",result.retcode,result.deal,result.order);
请记住,为了使用获利水平和止损水平,还要考虑同时使用修改TP和SL,否则将出现错误。
我已经将stopLoss水平添加到所有的斐波那契水平,所以结果可能与最初的开始有所不同。阅读此代码后,您将能够修改代码并更改这些水平。
结论
这种策略在某些时间段内可能有利可图,但在其他情况下也可能表现不佳。在2008-2014年危机和2020年新冠肺炎危机期间,策略很糟糕,所以,如果你使用这个战略,就要考虑到这一点。
这一策略在非危机时期和趋势时期效果良好。
它给出的夏普比率为4,这意味着该策略至少是稳健的,评估风险调整后业绩的财务状况被认为非常好,接近于优秀。
这一策略可以作为其他策略的补充。
你可以根据自己的意愿修改这个策略,我相信它可以得到更好的结果。
请记住,本文的目的是帮助人们理解该策略。此策略不是直接使用的,如果使用,风险自负
我建议你在更多的时间段和其他交易品种上测试这个策略。
使用其他时间段可能会产生不同的结果。
本文由MetaQuotes Ltd译自英文
原文地址: https://www.mql5.com/en/articles/12809
注意: 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.


这可能是我读过的代码最差的文章了,我无意冒犯,只是就事论事。不要在真实账户上使用这种代码。
你认为这在做什么?
除期货和股票外,大多数符号都没有真实的交易量数据。在外汇交易中,它总是返回 1。最高总是 = 1。
然后,您使用这个指数(真实成交量获得的最高值)来获得高值:
您是在混合不应该混合的东西(除非您知道自己在做什么)。高 "价格值与实际成交量有什么关系?
无论如何,它总是与 High[1] 相同,这显然是您想要得到的。那为什么不直接得到它而要通过 iHighest 和实际成交量呢?
我就不多说了。您说:
这篇文章的目的是帮助人们了解如何用 MQL5 编程
如果有人想了解如何使用 MQL5 编程,我建议他尽量避免阅读这篇文章。
这可能是我读过的代码最差的文章了,我无意冒犯,只是就事论事。不要在真实账户上使用这种代码。
你认为这在做什么?
除期货和股票外,大多数符号都没有真实的交易量数据。在外汇交易中,它总是返回 1。最高总是 = 1。
然后,您就可以使用该指数(实际成交量获得的最高值)来获得高点值:
您将不应该混合的东西混合在一起(除非您知道自己在做什么)。高 "价格值与实际成交量有何关系?
无论如何,它总是与 High[1] 相同,这显然是您想要得到的。那为什么不直接得到它而要通过 iHighest 和实际成交量呢?
我就不多说了。您说:
这篇文章的目的是帮助人们了解如何使用 MQL5 编程
如果有人想了解如何使用 MQL5 编程,我建议他尽量避免阅读这篇文章。
我解释了策略,这就是我的目的。您可以编写自己的程序。这只是一个例子。我的情况是必须显示结果,这就是为什么我上传了一个简单的 EA。真正的目的是展示策略。
是的,你是对的,这对学习编程没有帮助,这只是为了展示一个策略。
同意阿兰的观点,这也是我见过的最糟糕的编码员。如果能帮上忙的话,下面是修复方法:(替换 OnTick() 函数的第一部分)
解释很清楚,但代码中有很多不必要的声明和行数。
我看不出 MA 条件与随机 条件与 MA 趋势指示在哪里进行比较。
请指出来,也许代码可以修改和简化。
我运行了 EA,遗憾的是它没有执行交易。