交易的本质与预测未来市场发展的必要性有关,因此潜在利润很大程度上取决于预测的准确性。在我的文章基于价格方向和运动速度的交易思路的开头,我描述了以下想法:每个运动都是以其方向、加速度和速度为特征的。这同样适用于外汇和其他市场的价格变动。
任何运动都具有一定的特征,即运动的开始、一定的速度、惯性和结束。成功的交易策略是那些能够尽快发现市场开始的波动并进入市场的策略,并且能够清楚地识别这一波动的结束。然而,没有任何一种策略能够以绝对概率确定入场和出场点,这是一个有利的机会和概率的问题。因此,在本文中,我们将考虑一种概率理论工具——相关性,它将应用于金融市场的框架中。
相关性是两个或多个随机变量(或可被视为具有某种可接受精度的随机量)之间的统计关系。一个或多个变量的变化导致其他相关变量的系统变化。两个随机变量相关性的数学度量是相关系数。如果一个随机变量的变化不会导致另一个随机变量的规则变化,但会导致该随机变量的另一个统计特征的变化,则这种关系不被视为相关性,尽管它是统计的。
相关系数值可以在-1到+1之间变化。相关值越接近1,所研究变量之间的相关性越高。如果该值趋于1,则相关性被认为是正的,如果该值趋于-1,则相关性为负。在正相关过程中,其中一个变量的增加导致第二个变量的增加,在负相关的情况下,一个值的增加会导致第二个值的减少。
换句话说,相关性有助于根据可用数据确定一个变量对第二个变量的依赖性。相关性如何在金融市场交易中发挥作用呢?
让我们看看图1和明显的下跌趋势区。
图1 下跌趋势示例。
从标记区域可以看出,从烛形1开始,大多数收盘价低于开盘价,每个收盘价都低于前一个收盘价。所以,价格是下跌的。从视觉上看,这是一种下跌趋势。但是你怎么能知道依赖性是否强大呢?此外,这一趋势并不完美:在烛形4号、6号和9号曾有过小规模的上涨尝试。相关性在这里有什么帮助?在这种情况下,相关系数将指示当前变化的强度。根据对相关系数随时间变化的观察,我们可以得出以下几个结论:
以下类型的相关性有助于定义分析变量之间的相关性:
图1显示了线性负相关的示例。接下来,我们考虑各种类型的计算和确定两个变量相互依赖性的方法。
线性相关系数(Pearson 相关系数)
这种计算方法允许根据变量的绝对值确定变量之间的直接关系。计算是有组织的,这样如果变量之间的关系是线性的,Pearson 系数就会显示出来。在金融市场的背景下,这种关系将意味着存在于一个或其他方向的时间流动。Pearson 相关系数的计算公式如下:
现在让我们计算图1中所示数据的 Pearson 相关系数,并测量结束价格随时间的依赖性。为此,让我们在表格中输入数据:
收盘价 | 烛形编号 |
---|---|
1.23406 | 1 |
1.22856 | 2 |
1.22224 | 3 |
1.22285 | 4 |
1.21721 | 5 |
1.21891 | 6 |
1.21773 | 7 |
1.21500 | 8 |
1.21546 | 9 |
1.20995 | 10 |
整个计算如下图所示。
图2 Pearson 相关系数的计算。
计算顺序如下:
Spearman 等级相关系数
这种计算方法可以确定随机变量之间的直接线性关系。评估不是基于分析元素的数值,而是基于相应的等级。它的值也可以在-1到1之间变化,绝对值表示互连的紧密性,符号表示两个元素之间连接的方向。计算公式如下:
其中 Di 是所研究特征的等级差。让我们考虑一个计算图1中所示数据等级相关性的示例,并在新表中输入值:
收盘价 | 烛形编号 | 收盘价等级 | 烛形编号等级 |
---|---|---|---|
1.23406 | 1 | 10 | 1 |
1.22856 | 2 | 9 | 2 |
1.22224 | 3 | 7 | 3 |
1.22285 | 4 | 8 | 4 |
1.21721 | 5 | 4 | 5 |
1.21891 | 6 | 6 | 6 |
1.21773 | 7 | 5 | 7 |
1.21500 | 8 | 2 | 8 |
1.21546 | 9 | 3 | 9 |
1.20995 | 10 | 1 | 10 |
从表中可以看出,我们对收盘价的值进行了排名:排名1被指定为最低值,依此类推。利用该公式计算了所研究特征的D阶差,并将所得值代入公式中。
图3 Spearman 等级相关系数的计算。
如图3所示,我们找到等级的差异,然后找到产生差异的平方和,得到320,将所得值代入公式,得到-0.93939的结果。
根据相关系数的结果,我们可以得出同样的结论:强线性负相关。在这种情况下,这种联系的密切程度与 Pearson 相关系数相当。但应考虑以下事实:这种计算方法有一个缺点,差异的不可比值可以对应于等级差异的相同值。例如,柱的等级是可比的,而价格等级的值是不均匀的,尽管价格的变化很小,相差千分之几。因此,这种计算方法在这种情况下是合适的。
Kendall 等级相关系数
与 Spearman 系数一样,Kendall 等级相关系数是随机变量之间线性关系的度量。虽然计算方法不同,但分析元素的值的排序类似。此处采用以下系数计算公式:
其中 P 是匹配的和,而 Q 是反转的和。为了理解上述含义,让我们再次查看图1中的示例。首先,让我们按照以下方式对表数据进行排序:
收盘价 | 烛形编号 | 收盘价等级 | 烛形编号等级 |
---|---|---|---|
1.20995 | 10 | 1 | 10 |
1.21500 | 8 | 2 | 8 |
1.21546 | 9 | 3 | 9 |
1.21721 | 5 | 4 | 5 |
1.21773 | 7 | 5 | 7 |
1.21891 | 6 | 6 | 6 |
1.22224 | 3 | 7 | 3 |
1.22285 | 4 | 8 | 4 |
1.22856 | 2 | 9 | 2 |
1.23406 | 1 | 10 | 1 |
这样,表格就根据收盘价等级排好序了,之后,让我们确定比当前排名高的排名数,从“烛形编号等级”字段中的第一行开始。第一个值是10,所以让我们检查一下-没有排名高于1的,然后查看8并找到更高级别的9,以此类推。将会有 P 的匹配值。
然后我们计算低的等级。对于10,将有9个更低的等级,因为它是最高的一个。而 8 将有7个更低的等级 — 5,7,6,3,4,2,1. 这些将会是 Q 反转。让我们将结果值添加到表中并计算系数:
图4 Kendall 等级相关系数的计算。
然后我们总结出匹配值和反演值,它们的差等于 -37。通过将该值插入公式中,我们得到 Kendall 系数等于 -0,82。这又是一个很强的负相关。然而,结果表明,该方法比前两种方法更具选择性,因为绝对值较小。
Fechner符号相关系数
该方法是基于对平均值偏离方向上的一致性程度的评估,以及对与该值对应的偏离符号的计算。计算公式非常简单:
这里 Na 是用符号表示的匹配数,Nb是不匹配数。现在让我们从图1中计算示例的相关系数。
图4 Fechner 相关系数的计算。
计算如下:
如你所见,Fechner 相关系数的计算方法相当简单。结果值为 -0,8. 这再次表明,收盘价与烛形编号之间存在着强烈的负相关关系。
现在,让我们使用MQL5语言实现所有相关计算方法。
Pearson 相关系数
由于 Pearson 系数是用大公式计算的,所以计算分为两个阶段:分子和分母的计算。
//+------------------------------------------------------------------+ //| 分子的计算 | //+------------------------------------------------------------------+ double Numerator(double &Ranks[],int N) { //---- double Y[],dx[],dy[],mx=0.0,my=0.0,sum=0.0,sm=0.0; ArrayResize(Y,N); ArrayResize(dx,N); ArrayResize(dy,N); int n=N; for(int i=0; i<N; i++) { Y[i]=n; n--; } mx=Average(Y); my=Average(Ranks); for(int j=0;j<N;j++) { dx[j]=Y[j]-mx; dy[j]=Ranks[j]-my; sm+=dx[j]*dy[j]; } return sm; } //+------------------------------------------------------------------+ //| 分母的计算 | //+------------------------------------------------------------------+ double Denominator(double &Ranks[],int N) { //---- double Y[],dx2[],dy2[],mx=0.0,my=0.0,sum=0.0,smx2=0.0,smy2=0.0; ArrayResize(Y,N); ArrayResize(dx2,N); ArrayResize(dy2,N); int n=N; for(int i=0; i<N; i++) { Y[i]=n; n--; } mx=Average(Y); my=Average(Ranks); for(int j=0;j<N;j++) { dx2[j]=MathPow(Y[j]-mx,2); dy2[j]=MathPow(Ranks[j]-my,2); smx2+=dx2[j]; smy2+=dy2[j]; } return(MathSqrt(smx2*smy2)); }
指标可视化的最终计算方法和计算逻辑如下:
//+------------------------------------------------------------------+ //| 自定义指标迭代函数 | //+------------------------------------------------------------------+ int OnCalculate(const int rates_total, // 当前分时历史中的柱数 const int prev_calculated,// 前一次调用时计算的柱数 const int begin, // 柱开始计数的索引 const double &price[] ) { if(rates_total<rangeN+begin) return(0); int limit; if(prev_calculated>rates_total || prev_calculated<=0) { limit=rates_total-2-rangeN-begin; if(begin>0) PlotIndexSetInteger(0,PLOT_DRAW_BEGIN,rangeN+begin); } else limit=rates_total-prev_calculated; ArraySetAsSeries(price,true); for(int i=0; i<=limit; i++) { for(int k=0; k<rangeN; k++) PriceInt[k]=price[k+i]; ExtLineBuffer[i]=PearsonCalc(PriceInt,rangeN); } return(rates_total); } //+------------------------------------------------------------------+ //| 计算 Pearson 相关系数 | //+------------------------------------------------------------------+ double PearsonCalc(double &Ranks[],int N) { double ch,zn; ch=Numerator(Ranks,N); zn=Denominator(Ranks,N); return (ch/zn); }
Spearman 等级相关系数
对于基于此计算方法的指标,我使用了一些这里现有的解决方案。
//+------------------------------------------------------------------+ //| 自定义指标迭代函数 | //+------------------------------------------------------------------+ int OnCalculate(const int rates_total, // 当前分时历史中的柱数 const int prev_calculated,// 前一次调用时计算的柱数 const int begin, // 柱开始计数的索引 const double &price[] ) { if(rates_total<rangeN+begin) return(0); int limit; if(prev_calculated>rates_total || prev_calculated<=0) { limit=rates_total-2-rangeN-begin; if(begin>0) PlotIndexSetInteger(0,PLOT_DRAW_BEGIN,rangeN+begin); } else limit=rates_total-prev_calculated; ArraySetAsSeries(price,true); for(int i=limit; i>=0; i--) { for(int k=0; k<rangeN; k++) PriceInt[k]=int(price[i+k]*multiply); RankPrices(TrueRanks,PriceInt); ExtLineBuffer[i]=SpearmanCalc(R2,rangeN); } return(rates_total); } //+------------------------------------------------------------------+ //| 计算 Spearman 相关系数 | //+------------------------------------------------------------------+ double SpearmanCalc(double &Ranks[],int N) { //---- double sumd2=0.0; for(int i=0; i<N; i++) sumd2+=MathPow(Ranks[i]-i-1,2); return(1-6*sumd2/(N*(MathPow(N,2)-1))); }
Kendall 等级相关系数
对于这种方法的计算,让我们使用 MQL5 本身的内部储备。也就是说,我们将使用内置的数理统计库。
//+------------------------------------------------------------------+ //| 自定义指标迭代函数 | //+------------------------------------------------------------------+ int OnCalculate(const int rates_total, // 当前分时历史中的柱数 const int prev_calculated,// 前一次调用时计算的柱数 const int begin, // 柱开始计数的索引 const double &price[] ) { if(rates_total<rangeN+begin) return(0); int limit; if(prev_calculated>rates_total || prev_calculated<=0) { limit=rates_total-2-rangeN-begin; if(begin>0) PlotIndexSetInteger(0,PLOT_DRAW_BEGIN,rangeN+begin); } else limit=rates_total-prev_calculated; ArraySetAsSeries(price,true); for(int i=0; i<=limit; i++) { for(int k=0; k<rangeN; k++) PriceInt[k]=price[k+i]; ExtLineBuffer[i]=KendallCalc(PriceInt,rangeN); } return(rates_total); } //+------------------------------------------------------------------+ //| 计算 Kendall 相关系数 | //+------------------------------------------------------------------+ double KendallCalc(double &Ranks[],int N) { double Y[],t; ArrayResize(Y,N); int n=N; for(int i=0; i<N; i++) { Y[i]=n; n--; } MathCorrelationKendall(Ranks,Y,t); return (t); } //+------------------------------------------------------------------+
Fechner符号相关系数
该方法是在计算偏离平均值的匹配符号的基础上进行的。此外,对匹配符号进行计数。
//+------------------------------------------------------------------+ //| 自定义指标迭代函数 | //+------------------------------------------------------------------+ int OnCalculate(const int rates_total, // 当前分时历史中的柱数 const int prev_calculated,// 前一次调用时计算的柱数 const int begin, // 柱开始计数的索引 const double &price[] ) { if(rates_total<rangeN+begin) return(0); int limit; if(prev_calculated>rates_total || prev_calculated<=0) { limit=rates_total-2-rangeN-begin; if(begin>0) PlotIndexSetInteger(0,PLOT_DRAW_BEGIN,rangeN+begin); } else limit=rates_total-prev_calculated; ArraySetAsSeries(price,true); for(int i=0; i<=limit; i++) { for(int k=0; k<rangeN; k++) PriceInt[k]=price[k+i]; ExtLineBuffer[i]=FechnerCalc(PriceInt,rangeN); } return(rates_total); } //+------------------------------------------------------------------+ //| 计算 Fechner 相关系数 | //+------------------------------------------------------------------+ double FechnerCalc(double &Ranks[],int N) { double Y[],res,mx,my,sum=0.0,markx[],marky[]; double Na=0.0,Nb=0.0; ArrayResize(Y,N); ArrayResize(markx,N); ArrayResize(marky,N); int n=N; for(int i=0; i<N; i++) { Y[i]=n; n--; } mx=Average(Y); my=Average(Ranks); for(int j=0; j<N; j++) { markx[j]=(Y[j]>mx)?1:-1; marky[j]=(Ranks[j]>my)?1:-1; if(markx[j]==marky[j]) Na++; else Nb++; } res=(Na-Nb)/(Na+Nb); return (res); }
图6展示了计算烛形收盘价与时间之间相关性的四种方法。所有指标的周期数都设置为10,因此您可以在类似条件下评估其运行情况。
图6 不同计算方法的比较。
在基于相关性创建交易策略时,应仔细分析指标的具体情况和计算方法,并识别可能存在的风险和不利的市场进入条件。
几乎所有指标都不建议使用大的周期数,因为指标将滞后。在急剧反转的情况下,它们也可能表现出弱相关性,因为它们将解释已经完成的反向运动的价值。
由于指标分析的不是当前价格,而是一段时间内多个值的总和,因此当前相关值不应被视为绝对估计值,而应当分析相关动态。这可以使用利用相关系数当前值和以前值的振荡指标来实现。
因此,我们的策略将分析基于相关系数的振荡指标的行为,并寻找一个突破预定水平。以下两个选项是可能的:
图7 交易相关系数的下降。
从截图中可以看出,有两个对称的相关系数水平:卖出水平0.3和买入水平0.3。当卖出水平向下被突破时,开启卖出订单。当买入水平被向上突破时,开启买入订单。
图8 交易相关系数的增长。
在图8所示的第二种交易模式中,关键的市场进入水平是相反的。这一次,当买入水平向上被突破时,开启一个买入订单。当卖出水平被向下突破时,开启一个卖出订单。
为了实现 EA 交易,我们需要提供上述策略,并选择适当的相关计算方法。
//+------------------------------------------------------------------+ //| 运行模式的枚举 | //+------------------------------------------------------------------+ enum Strategy_type { DECREASE = 1, //On Decrease INCREASE //On Increase }; //+------------------------------------------------------------------+ //| 相关性计算方法的枚举 | //+------------------------------------------------------------------+ enum Corr_method { PEARSON = 1, //Pearson SPEARMAN, //Spearman KENDALL, //Kendall FECHNER //Fechner };
除了EA参数外,我们还要添加以下输入:计算方法的选择、策略、关键水平和可调整的操作时间框架。
//+------------------------------------------------------------------+ //| EA 交易输入参数 | //+------------------------------------------------------------------+ input string Inp_EaComment="Correlation Strategy"; //EA 注释 input double Inp_Lot=0.01; //手数 input MarginMode Inp_MMode=LOT; //资金管理 //--- 相关性的计算 input Corr_method Inp_Corr_method=1; //相关性方法 input Strategy_type Inp_Strategy_type=1; //策略类型 //--- EA 参数 input string Inp_Str_label="===EA parameters==="; //标签 input int Inp_MagicNum=1111; //幻数 input int Inp_StopLoss=40; //止损(点数) input int Inp_TakeProfit=60; //获利(点数) //--- 指标参数 input int Inp_RangeN=10; //范围计算 input double Inp_KeyLevel=0.2; //关键水平 input ENUM_TIMEFRAMES Inp_Timeframe=PERIOD_CURRENT; //工作时间框架
在EA初始化步骤中将检查关键水平的正确性:水平必须位于0和1之间,因为水平将使用绝对值。
//--- 检查关键水平的正确性 if(Inp_KeyLevel>1 || Inp_KeyLevel<0) { Print(Inp_EaComment,": 错误的关键水平!"); return(INIT_FAILED); }
然后设置输入参数中选择的相关计算方法:
//--- switch(Inp_Corr_method) { case 1: ind_type="Correlation\\PearsonCorrelation"; break; case 2: ind_type="Correlation\\SpearmanCorrelation"; break; case 3: ind_type="Correlation\\KendallCorrelation"; break; case 4: ind_type="Correlation\\FechnerCorrelation"; break; default: break; } //--- 取得指标句柄 InpInd_Handle=iCustom(Symbol(),Inp_Timeframe,ind_type,Inp_RangeN); if(InpInd_Handle==INVALID_HANDLE) { Print(Inp_EaComment,": Failed to get indicator handle"); Print("Handle = ",InpInd_Handle," error = ",GetLastError()); return(INIT_FAILED); }
之后,我们可以设置进场条件和EA操作逻辑。
//+------------------------------------------------------------------+ //| EA交易分时函数 | //+------------------------------------------------------------------+ void OnTick() { //--- 取得用于计算的数据 if(!GetIndValue()) return; if(!Trade.IsOpenedByMagic(Inp_MagicNum)) { //--- 如果有买入信号,开启订单 if(BuySignal()) Trade.BuyPositionOpen(Symbol(),Inp_Lot,Inp_StopLoss,Inp_TakeProfit,Inp_MagicNum,Inp_EaComment); //--- 如果有卖出信号,开启订单 if(SellSignal()) Trade.SellPositionOpen(Symbol(),Inp_Lot,Inp_StopLoss,Inp_TakeProfit,Inp_MagicNum,Inp_EaComment); } } //+------------------------------------------------------------------+ //| 买入条件 | //+------------------------------------------------------------------+ bool BuySignal() { bool res=false; if(Inp_Strategy_type==1) res=(corr[1]>Inp_KeyLevel && corr[0]<Inp_KeyLevel)?true:false; else if(Inp_Strategy_type==2) res=(corr[0]>Inp_KeyLevel && corr[1]<Inp_KeyLevel)?true:false; return res; } //+------------------------------------------------------------------+ //| 卖出条件 | //+------------------------------------------------------------------+ bool SellSignal() { bool res=false; if(Inp_Strategy_type==1) res=(corr[1]<-Inp_KeyLevel && corr[0]>-Inp_KeyLevel)?true:false; else if(Inp_Strategy_type==2) res=(corr[0]<-Inp_KeyLevel && corr[1]>-Inp_KeyLevel)?true:false; return res; }
EA 交易将使用以下参数进行测试:
测试和优化参数.
图9 需要优化的参数集。
优化将有助于确定哪种计算方法和策略类型更可取。测试和优化产生了以下结果。
图10 测试和优化结果。
现在让我们使用最佳优参数测试EA。
图11 最佳参数测试结果。
根据所得结果,可进行以下观察:
附加的存档包含所有列出的文件,这些文件位于相应的文件夹中。为了使它们正确工作,您只需要把它们保存到终端文件夹的 MQL5 文件夹下。
文章中使用的程序
# | 名称 | 类型 | 声明 |
---|---|---|---|
1 | Correlation.mq5 | EA | 包括4种相关计算方法和2种基于它们的策略的 EA 交易。 |
2 | Trade.mqh | 代码库 | 交易函数类 |
3 | FechnerCorrelation.mq5 | 指标 | Fechner 相关系数计算指标 |
4 | KendallCorrelation.mq5 | 指标 | Kendall 相关系数计算指标 |
5 | PearsonCorrelation.mq5 | 指标 | Pearson 相关系数计算指标 |
6 | SpearmanCorrelation.mq5 | 指标 | Spearman 相关系数计算指标 |
本社区仅针对特定人员开放
查看需注册登录并通过风险意识测评
5秒后跳转登录页面...
移动端课程