
如何用 MQL5 创建自定义真实强度指数指标
概述
如果我们正确使用技术指标,它们会非常实用,因其也许会提供额外的见解,而这些见解很难通过查看价格行为来检测。 有许多现成的技术指标我们可以取用,但有时可能会发现我们需要自定义它们,以便为我们提供指示或特定的见解,或者我们需要根据自己的获胜思路创建一个新指标。 有一种途径能以 MQL5 创建此类自定义指标,并在 MetaTrader 5 交易平台中使用它们。 在本文中,我将与您分享如何从头开始创建真实强度指数指标。 我们将学习该指标的细节,并看到如何在我们的代码中计算它。 不仅如此,我们还将学习如何遵照交易策略,基于这个自定义指标创建智能系统。 我们将遍历以下主题涵盖所有这些内容:
在理解了前面提到的主题之后,我们就能够很好地理解如何使用和解释真实强度指数指标,并以 MQL5 语言计算和编写可在 MetaTrader 5 中使用的自定义指标。 您还可以在其它交易系统或 EA 中实现该指标。 如果您想拓展您的编程技能,我建议您尝试自行编写这里的内容,因为这种做法是任何学习过程或路线图中非常重要的一步。 我们将在 MetaTrader 5 内置的 IDE 中编写我们的 MQL5 代码。 如果您还没有该平台,或者您不知道如何下载和使用它,您可以阅读我之前文章中的 “在 MetaEditor 中编写 MQL5 代码” 主题。
真实强度指数(TSI)定义
在这一部分中,我们将辨别真实强度指数(TSI)技术指标,以便正确理解它。 它是由威廉·布劳(William Blau)开发的,是一款衡量价格行为动量的技术指标,即它衡量金融产品的强度,是强势亦或弱势。 它围绕零轴振荡,因此它是一个动量振荡器指标。 信号线也可与它一起使用,基于这些线之间的交叉获得额外的买入或卖出信号。 不过,我们只能基于 TSI 线获得信号,即它与零轴的交叉。 如果高于零轴,则表示看涨动量,如果低于零轴,则表示看跌动量。 它可用于检测超买和超卖区域,并检测看涨和看跌背离。 由于我们知道,我们需要确认其信号,以便提升证据的权重;最好将其与其它技术工具配合使用,我们应该在价格行为相同的背景下使用这些工具,从而获得更好的见解。
现在我们来看看如何计算这个指标。 计算分几个步骤进行,分别是:
计算双重平滑动量:
- 从当前价格中减去前一个价格来计算动量(价格变化)
- 依据已计算出的动量,计算其 25-周期 EMA,得到第一次平滑
- 依据第一次平滑(已计算动量的 25-周期 EMA)再次计算 13-周期 EMA,得到第二次平滑
计算双重平滑绝对动量:
- 从当前绝对价格中减去前期绝对价格来计算绝对动量
- 依据已计算出的绝对动量,计算其 25-周期 EMA,得到第一次平滑
- 依据第一次平滑(已计算绝对动量的 25-周期 EMA)再计算 13-周期 EMA,得到第二次平滑
计算 TSI = 100*(双重平滑动量 / 双重平滑绝对动量)
这种计算的后果是得到一条围绕零轴的振荡线,衡量价格行为的动量,并检测我们提到的超买和超卖区域。
自定义简单 TSI 指标
MQL5 编程语言有很多预定义的技术指标,我们可以在我们的系统中使用这些预定义的函数。 在本系列的前几篇文章中,我们已经讨论过很多这样的指标,讨论了如何基于这些流行的技术指标来设计交易系统。 您可以查阅以前的文章,也许会找到有用的东西。 现在的问题是,如果指标不存在,我们如何创建一个指标,就像标准平台交付包中一样,或者即使它存在,我们如何创建一个自定义指标来获得所需的信号或触发器。 简短的答案是利用主要编程语言创建一个自定义指标,这就是我们在这一部分中所要做的。
我们将学习如何利用 MQL5 创建我们的自定义真实强度指数指标。 然后,我们将在其它系统或 EA 中使用它的功能。 以下步骤就是为了创建此自定义指标。
创建其它参数要使用 #property,在它之后是我们指定的标识符和数值,以下这些是我们需要指定的参数:
- (indicator_separate_window) — 在单独的窗口中显示指标。
- (indicator_buffers) — 指定指标的缓冲区数量,我们将指定(8)。
- (indicator_plots) — 指定指标中绘图板的数量,我们将指定(1)。
- (indicator_label1) — 设置绘图板编号的标签,我们将指定(TSI)。
- (indicator_type1) — 从 ENUM_DRAW_TYPE 枚举值中选一个,来指定绘图板的类型,我们将指定(DRAW_LINE)。
- (indicator_color1) — 指定指标线的颜色,我们将指定(clrBlue)。
- (indicator_style1) — 指定指标线的样式,我们将指定(STYLE_SOLID)。
- (indicator_width1) — 指定指标线的粗细,我们将指定(3)。
#property indicator_separate_window #property indicator_buffers 8 #property indicator_plots 1 #property indicator_label1 "TSI" #property indicator_type1 DRAW_LINE #property indicator_color1 clrBlue #property indicator_style1 STYLE_SOLID #property indicator_width1 3
我们需要包含 MovingAverage.mqh 文件,以便在计算中调用它的组件,该文件存在于 Include 文件夹中,故需用 #include 命令。 请确保编写的文件名与实际文件名相同。
#include <MovingAverages.mqh>
我们需要输入两个平滑周期的设置,如果用户需要更新程序中指定的默认值,我们可用输入类输入这些值,并计算指标。 之后,我们用 uint(无符号整数型)声明我们需要的变量(InpSmPeriod1、InpSmPeriod2)的数据类型。 然后,我们将(25) 分配给 InpSmPeriod1,将(13)分配给 InpSmPeriod2,作为默认值。
input uint InpSmPeriod1 = 25; // Smoothing period 1 input uint InpSmPeriod2 = 13; // Smoothing period 2
创建两个整数型变量(smperiod1,smperiod2)保存平滑周期。
int smperiod1; int smperiod2;
为指标缓冲区创建 7 个数组
double indBuff[]; double momBuff[]; double momSmBuff1[]; double momSmBuff2[]; double absMomBuff[]; double absMomSmBuff1[]; double absMomSmBuff2[];
在 OnInit() 函数中,我们将执行以下步骤:
声明变量(smperiod1,smperiod2),如果用户输入的 InpSmPeriod1 和 InpSmPeriod2 小于 2,则返回值 2;如果是其它数值,则返回 InpSmPeriod1 和 InpSmPeriod2 的值。
smperiod1=int(InpSmPeriod1<2 ? 2 : InpSmPeriod1); smperiod2=int(InpSmPeriod2<2 ? 2 : InpSmPeriod2);
调用(SetIndexBuffer)函数将指标缓冲区与数组绑定。 其参数为:
index: 在我们的程序中设置指标缓冲区的数量,其为从 0 开始到 7 的数字。
buffer[]: 指定自定义指标中声明的数组。
data_type: 指定存储在指标数组中的数据类型。
SetIndexBuffer(0,indBuff,INDICATOR_DATA); SetIndexBuffer(2,momBuff,INDICATOR_CALCULATIONS); SetIndexBuffer(3,momSmBuff1,INDICATOR_CALCULATIONS); SetIndexBuffer(4,momSmBuff2,INDICATOR_CALCULATIONS); SetIndexBuffer(5,absMomBuff,INDICATOR_CALCULATIONS); SetIndexBuffer(6,absMomSmBuff1,INDICATOR_CALCULATIONS); SetIndexBuffer(7,absMomSmBuff2,INDICATOR_CALCULATIONS);
设置相应指标属性的数值时,指标属性(prop value)必须是字符串类型,方法是调用(IndicatorSetString)函数,仅按指定属性标识符调用该函数的不同变体。 此步骤设置指标的短名称,并确定在指标窗口左上角显示的周期。 其参数:
- prop_id: 指定指示器属性的标识符,该属性将是(ENUM_CUSTOMIND_PROPERTY_STRING)枚举之一。 在我们的程序中,它应是(INDICATOR_SHORTNAME)。
- prop_value: 指定属性的值,该值将为字符串数据类型。 它将是 "True Strength Index ("+(string)smperiod1+","+(string)smperiod2+")"。
IndicatorSetString(INDICATOR_SHORTNAME,"True Strength Index ("+(string)smperiod1+","+(string)smperiod2+")");
以整数型数据类型设置指标的另一个属性值,调用 (IndicatorSetInteger)变体设置指定属性标识符,将指标的小数值规范化到指定位数。 其参数
- prop_id: 指定指标属性的标识符,该属性应是(ENUM_CUSTOMIND_PROPERTY_INTEGER)枚举之一。 在我们的程序中,它应是(INDICATOR_DIGITS)。
- prop_value: 指定属性的值,该值是整数型数据类型。 它将与 Digits() 对应。
IndicatorSetInteger(INDICATOR_DIGITS,Digits());
调用(ArraySetAsSeries)为数组设置 AS_SERIES 标志。
ArraySetAsSeries(indBuff,true); ArraySetAsSeries(momBuff,true); ArraySetAsSeries(momSmBuff1,true); ArraySetAsSeries(momSmBuff2,true); ArraySetAsSeries(absMomBuff,true); ArraySetAsSeries(absMomSmBuff1,true); ArraySetAsSeries(absMomSmBuff2,true);
在 OnCalculate 函数之后,
- 我们为 close 数组设置 AS_SERIES 标志,然后检查 rates_total 是否小于 2,若是则返回 0。
- 创建一个整数变量(limit),令其等于(rates_total-prev_calculated)。
- 检查 limit 变量是否大于 1,若是的情况下,我们需要执行以下步骤:
- 取(rates_total - 2)的结果更新 limit 变量。
- 调用(ArrayInitialize)函数以预设值初始化 double 类型的数值数组。 其参数是 array[],对应应该初始化的指定数组,另一个参数是 value,指定所设置的新值。
ArraySetAsSeries(close,true); if(rates_total<2) return 0; int limit=rates_total-prev_calculated; if(limit>1) { limit=rates_total-2; ArrayInitialize(indBuff,EMPTY_VALUE); ArrayInitialize(momBuff,0); ArrayInitialize(momSmBuff1,0); ArrayInitialize(momSmBuff2,0); ArrayInitialize(absMomBuff,0); ArrayInitialize(absMomSmBuff1,0); ArrayInitialize(absMomSmBuff2,0); }
创建一个循环来更新 momBuff[i]、absMomBuff[i]。 此步骤中用到的新函数包括:
- (for)循环操作,其中包含三个表达式和一个可执行运算符。
- IsStopped() 检查 mql5 程序是否被强制关闭。
- MathAbs 返回绝对值(模数),我们可以使用 fabs() 函数来获得相同的结果。
for(int i=limit; i>=0 && !IsStopped(); i--) { momBuff[i]=close[i]-close[i+1]; absMomBuff[i]=MathAbs(momBuff[i]); }
调用(MovingAverage)Include 文件中的 ExponentialMAOnBuffer 函数来检查以下内容。
if(ExponentialMAOnBuffer(rates_total,prev_calculated,0,smperiod1,momBuff,momSmBuff1)==0) return 0; if(ExponentialMAOnBuffer(rates_total,prev_calculated,0,smperiod1,absMomBuff,absMomSmBuff1)==0) return 0; if(ExponentialMAOnBuffer(rates_total,prev_calculated,smperiod1,smperiod2,momSmBuff1,momSmBuff2)==0) return 0; if(ExponentialMAOnBuffer(rates_total,prev_calculated,smperiod1,smperiod2,absMomSmBuff1,absMomSmBuff2)==0) return 0;
创建另一个 for 循环来更新 indBuff[i] 变量
for(int i=limit; i>=0 && !IsStopped(); i--) indBuff[i]=(absMomSmBuff2[i]!=0 ? 100.0*momSmBuff2[i]/absMomSmBuff2[i] : 0);
在程序的末尾,是 return (rates_total) 函数
return(rates_total);
如此这般,现在我们完成了创建 TSI 自定义指标的代码,您还可以编辑代码的首选项,以便进行更多自定义。 以下是在一个模块当中创建的指标完整代码:
//+------------------------------------------------------------------+ //| simple TSI.mq5 | //| Copyright 2023, MetaQuotes Ltd. | //| https://www.mql5.com | //+------------------------------------------------------------------+ #property copyright "Copyright 2023, MetaQuotes Ltd." #property link "https://www.mql5.com" #property version "1.00" #property indicator_separate_window #property indicator_buffers 8 #property indicator_plots 1 #property indicator_label1 "TSI" #property indicator_type1 DRAW_LINE #property indicator_color1 clrBlue #property indicator_style1 STYLE_SOLID #property indicator_width1 3 #include <MovingAverages.mqh> input uint InpSmPeriod1 = 25; // Smoothing period 1 input uint InpSmPeriod2 = 13; // Smoothing period 2 int smperiod1; int smperiod2; double indBuff[]; double momBuff[]; double momSmBuff1[]; double momSmBuff2[]; double absMomBuff[]; double absMomSmBuff1[]; double absMomSmBuff2[]; int OnInit() { smperiod1=int(InpSmPeriod1<2 ? 2 : InpSmPeriod1); smperiod2=int(InpSmPeriod2<2 ? 2 : InpSmPeriod2); SetIndexBuffer(0,indBuff,INDICATOR_DATA); SetIndexBuffer(2,momBuff,INDICATOR_CALCULATIONS); SetIndexBuffer(3,momSmBuff1,INDICATOR_CALCULATIONS); SetIndexBuffer(4,momSmBuff2,INDICATOR_CALCULATIONS); SetIndexBuffer(5,absMomBuff,INDICATOR_CALCULATIONS); SetIndexBuffer(6,absMomSmBuff1,INDICATOR_CALCULATIONS); SetIndexBuffer(7,absMomSmBuff2,INDICATOR_CALCULATIONS); IndicatorSetString(INDICATOR_SHORTNAME,"True Strength Index ("+(string)smperiod1+","+(string)smperiod2+")"); IndicatorSetInteger(INDICATOR_DIGITS,Digits()); ArraySetAsSeries(indBuff,true); ArraySetAsSeries(momBuff,true); ArraySetAsSeries(momSmBuff1,true); ArraySetAsSeries(momSmBuff2,true); ArraySetAsSeries(absMomBuff,true); ArraySetAsSeries(absMomSmBuff1,true); ArraySetAsSeries(absMomSmBuff2,true); return(INIT_SUCCEEDED); } 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[]) { ArraySetAsSeries(close,true); if(rates_total<2) return 0; int limit=rates_total-prev_calculated; if(limit>1) { limit=rates_total-2; ArrayInitialize(indBuff,EMPTY_VALUE); ArrayInitialize(momBuff,0); ArrayInitialize(momSmBuff1,0); ArrayInitialize(momSmBuff2,0); ArrayInitialize(absMomBuff,0); ArrayInitialize(absMomSmBuff1,0); ArrayInitialize(absMomSmBuff2,0); } for(int i=limit; i>=0 && !IsStopped(); i--) { momBuff[i]=close[i]-close[i+1]; absMomBuff[i]=MathAbs(momBuff[i]); } if(ExponentialMAOnBuffer(rates_total,prev_calculated,0,smperiod1,momBuff,momSmBuff1)==0) return 0; if(ExponentialMAOnBuffer(rates_total,prev_calculated,0,smperiod1,absMomBuff,absMomSmBuff1)==0) return 0; if(ExponentialMAOnBuffer(rates_total,prev_calculated,smperiod1,smperiod2,momSmBuff1,momSmBuff2)==0) return 0; if(ExponentialMAOnBuffer(rates_total,prev_calculated,smperiod1,smperiod2,absMomSmBuff1,absMomSmBuff2)==0) return 0; for(int i=limit; i>=0 && !IsStopped(); i--) indBuff[i]=(absMomSmBuff2[i]!=0 ? 100.0*momSmBuff2[i]/absMomSmBuff2[i] : 0); return(rates_total); }
在编译此代码,且没有错误后,我们在导航器的 Indicators 文件夹中的可用指标中找到该指标,并将其拖放到所需的图表上,然后会发现指标的窗口和输入,如下所示:
故此,我们有两个输入,可由用户确定第一个平滑周期的默认值为 25,第二个平滑周期的默认值为 13。 但是,正如我们提到的,用户仍然可以根据自己的喜好编辑它们。
我们可以在上一张图片中看到,在颜色选项卡中,用户可以选择 TSI 线的颜色、宽度和样式。 在确定指标的喜好输入和样式后,我们可以在图表上找到与以下相同的指标:
正如我们在上一张图表中看到的,我们在价格下方的单独窗口中得到 TSI 指标线,这条线围绕零轴振荡。 我们有指标的标签、它的平滑周期和指标的值。
自定义 TSI EA
在这一部分中,我们将以一种非常简单的方式学习如何在自动化系统中使用我们的自定义指标,如此即可在触发特定条件后,它可以生成特定的信号或举措。 我们将首先创建一个非常简单的系统,创建一个智能系统,它将在图表上生成一条含有 TSI 当前值的注释,然后再开发一个执行更复杂指令的 EA。
故此,以下就是创建我们的智能系统所需的步骤:
- 创建一个整数型变量(TSI)。
- 调用 iCustom 函数定义 TSI,并返回指标的句柄,其参数为:
- symbol:指定品种名称,我们将采用对应当前品种的 _Symbol。
- period: 指定时间帧,我们将采用对应当前时间帧的 _period。
- name: 指定自定义指标的路径。
- 一旦 EA 被删除,在 OnDeinit(const int reason) 部分打印 “TSI System Removed” 的文本。
- 创建一个 tsiVal[] 数组
- 调用 CopyBuffer 函数获取 TSI 指标缓冲区的数据,该函数变体可按第一个位置调取所需的元素数量。 其参数为:
- indicator_handle: 指定返回的指标句柄,我们将采用(TSI)内的值。
- buffer_num: 指定指标缓冲区编号,我们将采用(0)。
- start_pos: 指定要复制的起始位置,我们将采用(0)。
- count: 指定要复制的数据计数,我们将采用(1)。
- buffer[]: 指定要复制的数组,我们将使用(tsiVal)对应的值。
- 调用 Comment 函数显示当前 TSI 值转换的字符串,方法是调用(DoubleToString)函数,其参数 value 指定当前 TSI 值,且 digit 指定小数位数,我们将采用(_Digits)对应当前值。
完整代码如下:
//+------------------------------------------------------------------+ //| customSimpleTSI-EA.mq5 | //| Copyright 2023, MetaQuotes Ltd. | //| https://www.mql5.com | //+------------------------------------------------------------------+ #property copyright "Copyright 2023, MetaQuotes Ltd." #property link "https://www.mql5.com" #property version "1.00" int TSI; int OnInit() { TSI=iCustom(_Symbol,_Period,"My Files\\TSI\\simpleTSI"); return(INIT_SUCCEEDED); } void OnDeinit(const int reason) { Print("TSI System Removed"); } void OnTick() { double tsiVal[]; CopyBuffer(TSI,0,0,1,tsiVal); Comment("TSI Value ",DoubleToString(tsiVal[0],_Digits)); }
编译此代码,无错误,并执行后,它将加载到图表上。 以下是其测试信号的示例:
正如我们所看到的,我们在图表左上角得到一条注释,其中包含当前的 TSI 值。 如果我们想确保信号与我们基于指标的信号相同,我们可以加载 EA,并同时插入指标值。 以下是为了确保我们的工作:
正如我们在右上角看到的,我们加载了 EA,当前 TSI 值出现在左上角,同时我们加载 TSI 指标作为图表价格下方的单独窗口,其值与上面左上角那一行的 EA 信号相同。
TSI 系统 EA
在这一部分中,我们将基于创建的自定义 TSI 指标开发一个 EA,从而根据特定策略获取信号。 请注意,我们将要讨论的策略仅用于教学目的。 无论如何,它都需要优化,就像任何其它策略一样。 故此,您必须在将其用于真实帐户之前对其进行测试,并确保它对您有用。
我们将使用自定义 TSI 指标,与两条移动平均线相结合,根据特定策略获取买入和卖出信号,如下所示:
除了我们自定义的 TSI 指标外,我们还用到两条简单移动平均线,一条是快速的,周期为 10,另一条是慢速的,周期为 20。 如果前期的快速均线值小于前期的慢速均线值,同时当期快速均线值大于当期的慢速均线值,这意味着我们得到一个看涨的均线交叉,那么我们应检查当期的 TSI 值是否大于零,我们需要在图表上将得到的买入信号作为注释。 如果前期的快速均线值大于前期的慢速均线值,同时当期快速均线值小于当期的慢速均线值,这意味着我们得到一个看跌的均线交叉,那么我们应检查当期的 TSI 值是否小于零,我们需要在图表上将得到的卖出信号作为注释。
简单说,
如果 fastMA[1]>slowMA[1] && fastMA[0]<slowMA[0] && tsiVal[0]<0 ==> 卖出信号
以下是创建此类系统的完整代码:
//+------------------------------------------------------------------+ //| TSI System EA.mq5 | //| Copyright 2023, MetaQuotes Ltd. | //| https://www.mql5.com | //+------------------------------------------------------------------+ #property copyright "Copyright 2023, MetaQuotes Ltd." #property link "https://www.mql5.com" #property version "1.00" input ENUM_MA_METHOD inpMAType = MODE_SMA; //Moving Average Type input ENUM_APPLIED_PRICE inpPriceType = PRICE_CLOSE; //Price type input int inpFastMAPeriod = 10; // Fast moving average period input int inpSlowMAPeriod = 20; //Slow moving average period int tsi; double fastMAarray[], slowMAarray[]; int OnInit() { tsi=iCustom(_Symbol,_Period,"My Files\\TSI\\simpleTSI"); return(INIT_SUCCEEDED); } void OnDeinit(const int reason) { Print("TSI System Removed"); } void OnTick() { double tsiVal[]; CopyBuffer(tsi,0,0,1,tsiVal); int fastMA =iMA(_Symbol,_Period,inpFastMAPeriod,0,inpMAType,inpPriceType); int slowMA =iMA(_Symbol,_Period,inpSlowMAPeriod,0,inpMAType,inpPriceType); ArraySetAsSeries(fastMAarray,true); ArraySetAsSeries(slowMAarray,true); CopyBuffer(fastMA,0,0,3,fastMAarray); CopyBuffer(slowMA,0,0,3,slowMAarray); if(fastMAarray[1]<slowMAarray[1]&&fastMAarray[0]>slowMAarray[0]) { if(tsiVal[0]>0) { Comment("Buy Signal", "\nTSI Value ",DoubleToString(tsiVal[0],_Digits), "\nfastMA ",DoubleToString(fastMAarray[0],_Digits), "\nslowMA ",DoubleToString(slowMAarray[0],_Digits)); } } if(fastMAarray[1]>slowMAarray[1]&&fastMAarray[0]<slowMAarray[0]&&tsiVal[0]<0) { if(tsiVal[0]<0) { Comment("Sell Signal", "\nTSI Value ",DoubleToString(tsiVal[0],_Digits), "\nfastMA ",DoubleToString(fastMAarray[0],_Digits), "\nslowMA ",DoubleToString(slowMAarray[0],_Digits)); } } }
此代码中的差异与以下内容相同:
用户为移动平均线类型、应用价格类型、快速 MA 周期和慢速 MA 周期创建四个输入,并为其分配默认值。
input ENUM_MA_METHOD inpMAType = MODE_SMA; //Moving Average Type input ENUM_APPLIED_PRICE inpPriceType = PRICE_CLOSE; //Price type input int inpFastMAPeriod = 10; // Fast moving average period input int inpSlowMAPeriod = 20; //Slow moving average period
为快速 MA 数组和慢速 MA 创建两个数组。
double fastMAarray[], slowMAarray[];
调用预定义的 iMA 函数定义两条移动平均线,并返回移动平均线的句柄,其参数为:
- symbol: 我们将采用 _Symbol 对应当前品种。
- period: 我们将采用对应当前时间帧的 _period。
- ma_period: 我们将取用户输入的快速和慢速均线周期。
- ma_shift: 我们将采用(0),因为不需要位移
- ma_method: 我们将取用户输入作为 MA 类型。
- applied _price: 我们将取用户输入的价格类型。
int fastMA =iMA(_Symbol,_Period,inpFastMAPeriod,0,inpMAType,inpPriceType); int slowMA =iMA(_Symbol,_Period,inpSlowMAPeriod,0,inpMAType,inpPriceType);
调用 ArraySetAsSeries 函数为慢速和快速 MA 设置 AS_SERIES 标志
ArraySetAsSeries(fastMAarray,true); ArraySetAsSeries(slowMAarray,true);
调用 CopyBuffer 函数从两条移动平均线的缓冲区获取数据
CopyBuffer(fastMA,0,0,3,fastMAarray); CopyBuffer(slowMA,0,0,3,slowMAarray);
定义策略条件,
买入信号情况:
如果前期 fastMA 小于前期 slowMA,且当期 fastMA 大于当期 slowMA,同时当期 tsiVal 大于零,我们需要 EA 将买入信号作为图表上的注释返回,顺序如下:
- 买入信号
- TSI 数值
- fastMA 数值
- slowMA 数值
if(fastMAarray[1]<slowMAarray[1]&&fastMAarray[0]>slowMAarray[0]) { if(tsiVal[0]>0) { Comment("Buy Signal", "\nTSI Value ",DoubleToString(tsiVal[0],_Digits), "\nfastMA ",DoubleToString(fastMAarray[0],_Digits), "\nslowMA ",DoubleToString(slowMAarray[0],_Digits)); } }
卖出信号情况:
如果前期 fastMA 大于前期 slowMA,而当期 fastMA 小于当期 slowMA,同时当期 tsiVal 小于零,我们需要 EA 将卖出信号作为图表上的注释返回,顺序如下:
- 卖出信号
- TSI 数值
- fastMA 数值
- slowMA 数值
if(fastMAarray[1]>slowMAarray[1]&&fastMAarray[0]<slowMAarray[0]&&tsiVal[0]<0) { if(tsiVal[0]<0) { Comment("Sell Signal", "\nTSI Value ",DoubleToString(tsiVal[0],_Digits), "\nfastMA ",DoubleToString(fastMAarray[0],_Digits), "\nslowMA ",DoubleToString(slowMAarray[0],_Digits)); } }
编译此代码,没有错误,并拖放执行,获取其信号后,我们可以在“输入”选项卡中找到与如下窗口:
正如我们所看到的,我们有 MA 类型、价格类型、快速 MA 周期和慢速 MA 周期的四个输入。 设置好首选项,并按确定后,我们可以发现 EA 已加载到图表上,其信号如下:
买入信号情况
正如我们在上图中看到的,根据我们的策略条件,我们在左上角有一个买入信号作为注释,如下所示:
- 买入信号
- TSI 数值
- fastMA 数值
- slowMA 数值
卖出信号情况:
正如我们在上图中看到的,根据我们的策略条件,我们在左上角有一个卖出信号作为注释。 如下:
- 卖出信号
- TSI 数值
- fastMA 数值
- slowMA 数值
结束语
在本文中,我们学习了如何创建自己的真实强度指数技术指标,来实现您的特定设置和偏好。 我们已经看到了该指标提供了哪些信息和见解,这些信息和见解在交易中非常有帮助。 我们还学习了如何在一个简单的交易系统中使用此自定义指标,来取 TSI 指标的当前值生成图表上的注释。 我们还通过创建 EA 来了解如何在自动交易系统中使用该指标,该 EA 利用 TSI 数据与另一种技术工具相结合,在我们的例子中是移动平均线。 自定义 TSI 和两条移动平均线的组合后,根据特定策略生成买入和卖出信号,我们在 TSI 系统 EA 主题中详细研究了这些信号。
希望本文对您的交易和编程学习有所帮助。 如果您想阅读有关指标的其它文章,并学习如何基于最流行的技术指标创建交易系统,请参阅我之前的文章,其中我介绍了流行的指标,例如移动平均线、布林带、RSI、MACD、随机指标、抛物线转向指标、ATR、等等。
本文由MetaQuotes Ltd译自英文
原文地址: https://www.mql5.com/en/articles/12570
注意: 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.

首先,感谢您的文章。
我只看到创建了 7 个指标缓冲区,但却申报了 8 个。
是你计算错误吗?
您好,很棒的论文、
为什么在底部 2 个函数中填写的是 smperiod 1,而不是 0。