目录
- 简介
- 发现问题
- 价格速度测量方法
- 基于测量方法制定交易策略
- 测试价格速度测量方法
- 总结
- 结论
简介
市场研究和分析有多种不同的方法,主要是技术分析和基础分析。在技术分析中,交易者收集、处理和分析与市场有关的数字数据和参数,包括价格、数量等。在基本面分析中,交易者分析直接或间接影响市场的事件和新闻。在分析基础数据时,主要问题是解释一个事件对市场的影响。这种解释可能会受到交易员的意见和期望的影响。相反,技术数据通常不会产生各种解释,然而,在评估技术分析结果时也存在人为因素。
发现问题
说到市场的技术分析,我们指的是使用来自不同科学领域的任何评估系统,无论是数学还是物理。数学在技术分析中的必要性是相当明确的,而将各种物理现象应用于市场则更为有趣。因此,在本文中,我们将考虑这样一个众所周知的物理现象,即速度,它是一个单位时间内运动的度量。当我们看外汇和其他图表时,我们不仅可以清楚地看到当前的价格方向,而且可以看到它的速度,特别是在高波动期。任何人都可以直观地分析图表,并确定当前的价格变化是快还是慢。然而,由于人们的认知差异,这种视觉估计仍然具有高度的主观性。
现实生活中的一个简单例子是一个视觉技巧,在这个技巧中,一辆小型车辆似乎以较慢的速度移动,而一辆大型卡车似乎以较高的速度移动。这个问题是通过速度表来解决的,它以数字显示所有的东西,这对任何人来说都是完全相同的。
价格速度测量方法
在考虑各种测量价格速度的方法之前,让我们回想一下速度实际上是什么。
根据维基百科:
物体的速度是物体相对于一个参照系位置变化的速率,是时间的函数。速度是一个物理向量量;定义它需要大小和方向。
我们如何将这个定义应用于外汇?最简单的方法是用一个价格替换一个物体,并设定一个时间,在这个时间内形成一根棒子或一根蜡烛,作为参照系。从价格图的角度来看,如下所示:
图 1. 在 H1 时间段将价格显示为日式烛形
在这种情况下,简单的速度测量如下所示:
平均速度 = (收盘价格 — 开盘价格) / 小时数
关于价格随时间变化的确切观察和结论如下:
- 我们定义了价格每小时通过的平均点数。然而,测量只在一根蜡烛的框架内进行,我们无法在此框架内获得总体趋势图。
- 但是,如果我们将速度从每小时的点转换为每分钟的点、5分钟的点等,我们就可以在这个H1小时内获得更有价值的价格变动数据。
- 因此,下面两个结论是显而易见的:在较低的时间段上确定速度对于较高的时间段更为方便。当通过测量价格变动来测量当前时间段的平均速度时,我们应该使用几个烛形。
为了清楚起见,让我提供一个完美地说明上述结论的例子。图2显示了H1烛形,其每分钟平均速度是使用Average Speed(平均速度)指标计算得出的。这里等于每分钟2.53点。
图 2. 在 EURUSD H1 上计算平均速度
现在让我们来看看M15上的同样的烛形。
图 3. 在 EURUSD M15 上计算平均速度
图3显示,在所选小时的前15分钟(Average speed是 6.93)内,运动非常强烈,随后显著减速。当然,如果我们把平均速度的四个值加起来,每分钟得到相同的2.53点。因此,将蜡烛分解成组件可以让我们发现许多有关其动力学的数据。
将 H1 烛形分成 M1 间隔会产生更多的数据。
图 4. 在 EURUSD M1 上计算平均速度
还有另一种测量方法,用于计算M1时间段内点的移动速度,
它涉及即时(当前)价格速度。一方面,它的值总是尽可能的相关。另一方面,与分时价格变化配合使用的示例指标如下所示:
图 5. 即时价格速度指标
显然,评估这样一个混乱(尽管相关)的数据,以便随后在外汇交易中使用,是一个相当具有挑战性的任务。
如我们所知,绝大多数指标都是价格或其分析仪的衍生产品,这些是一些与价格速度相关的著名指标:
- 动量指标衡量一段时间内的价格变化量。极高或较低的动量值表示当前趋势的延续。这意味着与指标的较大偏差表示当前特定方向的高价格速度。
- ADX 趋势指标. 平均定向运动指数显示了当前趋势的强度。实际上,它显示当前的平均速度。
基于测量方法制定交易策略
因此,测试各种价格-速度测量的目标分为三大类:
- 以单位时间内通过点的比率直接测量平均速度。
- 以通过的点数与分时数之比来测量速度。
- 使用跟随趋势的和其他指标进行间接速度测量。
交易策略 1
要使用平均速度(Average Speed)指标根据测量速度(单位时间内通过的点数)来测试第一种方法,应将显示趋势方向的过滤器添加到测试策略中,因为指示器显示单位时间内的点数,而不管趋势方向。
我决定使用Coordinated ADX and MACD(协调的ADX和MACD,CAM)指标作为这样的过滤器。交易策略看起来是这样的:
参数 | 描述 |
---|---|
使用的指标 | 平均速度 |
使用的指标 | Coordinated ADX and MACD (CAM) |
时间框架 | 任意 |
买入条件 | 烛形为绿色,而平均速度值高于阈值(参数中预先设置)。 |
卖出条件 | 烛形颜色为红色,而平均速度值高于阈值(参数中预先设置)。 |
退场条件 | 获利/止损 |
图 6 显示了买入和卖出的建立
图 6. 交易策略的入场条件
策略是按照下面的方法实现的:
//+------------------------------------------------------------------+ //| EA 的输入参数 | //+------------------------------------------------------------------+ input string InpEaComment = "Strategy #1"; // EA Comment input int InpMagicNum = 1111; // Magic number input double InpLot = 0.1; // Lots input uint InpStopLoss = 400; // StopLoss in points input uint InpTakeProfit = 500; // TakeProfit in points input uint InpSlippage = 0; // Slippage in points input ENUM_TIMEFRAMES InpInd_Timeframe = PERIOD_H1; // Timeframe for the calculation //--- 平均速度指标的参数 input int InpBars = 1; // Days input ENUM_APPLIED_PRICE InpPrice = PRICE_CLOSE; // Applied price input double InpTrendLev = 2; // Trend Level //--- CAM 指标参数 input uint InpPeriodADX = 10; // ADX period input uint InpPeriodFast = 12; // MACD Fast EMA period input uint InpPeriodSlow = 26; // MACD Slow EMA period //--- CEngine engine; CTrade trade; //--- 声明指标变量和句柄 double lot; ulong magic_number; uint stoploss; uint takeprofit; uint slippage; int InpInd_Handle1,InpInd_Handle2; double avr_speed[],cam_up[],cam_dn[]; //+------------------------------------------------------------------+ //| EA 交易初始化函数 | //+------------------------------------------------------------------+ int OnInit() { //--- 初始化检查 if(!TerminalInfoInteger(TERMINAL_TRADE_ALLOWED)) { Print(InpEaComment,": 不允许进行交易!"); return(INIT_FAILED); } if(!TerminalInfoInteger(TERMINAL_CONNECTED)) { Print(InpEaComment,": 没有连接!"); return(INIT_FAILED); } //--- 取得 Average Speed 指标句柄 InpInd_Handle1=iCustom(Symbol(),InpInd_Timeframe,"Speed Price\\average_speed", InpBars, InpPrice ); if(InpInd_Handle1==INVALID_HANDLE) { Print(InpEaComment,": 获取 average_speed 句柄失败"); Print("Handle = ",InpInd_Handle1," error = ",GetLastError()); return(INIT_FAILED); } //--- 取得 CAM 指标句柄 InpInd_Handle2=iCustom(Symbol(),InpInd_Timeframe,"Speed Price\\CAM", InpPeriodADX, InpPeriodFast, InpPeriodSlow ); if(InpInd_Handle2==INVALID_HANDLE) { Print(InpEaComment,": 获取 average_speed 句柄失败"); Print("Handle = ",InpInd_Handle2," error = ",GetLastError()); return(INIT_FAILED); } //--- ArrayInitialize(avr_speed,0.0); ArrayInitialize(cam_up,0.0); ArrayInitialize(cam_dn,0.0); ArraySetAsSeries(avr_speed,true); ArraySetAsSeries(cam_up,true); ArraySetAsSeries(cam_dn,true); //--- 设置交易参数 lot=NormalizeLot(Symbol(),fmax(InpLot,MinimumLots(Symbol()))); magic_number=InpMagicNum; stoploss=InpStopLoss; takeprofit=InpTakeProfit; slippage=InpSlippage; //--- trade.SetDeviationInPoints(slippage); trade.SetExpertMagicNumber(magic_number); trade.SetTypeFillingBySymbol(Symbol()); trade.SetMarginMode(); trade.LogLevel(LOG_LEVEL_NO); //--- return(INIT_SUCCEEDED); } //+------------------------------------------------------------------+ //| 计时器函数 | //+------------------------------------------------------------------+ void OnTimer() { if(!MQLInfoInteger(MQL_TESTER)) engine.OnTimer(); } //+------------------------------------------------------------------+ //| EA交易分时函数 | //+------------------------------------------------------------------+ void OnTick() { //--- 如果在测试器中工作 if(MQLInfoInteger(MQL_TESTER)) engine.OnTimer(); if(!IsOpenedByMagic(InpMagicNum)) { //--- 取得计算数据 if(!GetIndValue()) return; //--- if(BuySignal()) { //--- 取得相对止损水平的正确止损和获利价格 double sl=CorrectStopLoss(Symbol(),ORDER_TYPE_BUY,0,stoploss); double tp=CorrectTakeProfit(Symbol(),ORDER_TYPE_BUY,0,takeprofit); //--- 开启买入仓位 trade.Buy(lot,Symbol(),0,sl,tp); } else if(SellSignal()) { //--- 取得相对止损水平的正确止损和获利价格 double sl=CorrectStopLoss(Symbol(),ORDER_TYPE_SELL,0,stoploss); double tp=CorrectTakeProfit(Symbol(),ORDER_TYPE_SELL,0,takeprofit); //--- 开启卖出仓位 trade.Sell(lot,Symbol(),0,sl,tp); } } } //+------------------------------------------------------------------+ //| | //+------------------------------------------------------------------+ bool BuySignal() { return(avr_speed[0]>=InpTrendLev && cam_up[0]!=EMPTY_VALUE)?true:false; } //+------------------------------------------------------------------+ bool SellSignal() { return(avr_speed[0]>=InpTrendLev && cam_dn[0]!=EMPTY_VALUE)?true:false; } //+------------------------------------------------------------------+ //| 取得当前指标值 | //+------------------------------------------------------------------+ bool GetIndValue() { return(CopyBuffer(InpInd_Handle2,0,0,1,cam_up)<1 || CopyBuffer(InpInd_Handle2,1,0,1,cam_dn)<1 || CopyBuffer(InpInd_Handle1,0,0,1,avr_speed)<1 )?false:true; } //+------------------------------------------------------------------+ //| 使用幻数检查开启的仓位 | //+------------------------------------------------------------------+ bool IsOpenedByMagic(int MagicNumber) { int pos=0; uint total=PositionsTotal(); //--- for(uint i=0; i<total; i++) { if(SelectByIndex(i)) if(PositionGetInteger(POSITION_MAGIC)==MagicNumber) pos++; } return((pos>0)?true:false); } //+------------------------------------------------------------------+ //| 根据索引选择仓位 | //+------------------------------------------------------------------+ bool SelectByIndex(const int index) { ENUM_ACCOUNT_MARGIN_MODE margin_mode=(ENUM_ACCOUNT_MARGIN_MODE)AccountInfoInteger(ACCOUNT_MARGIN_MODE); //--- if(margin_mode==ACCOUNT_MARGIN_MODE_RETAIL_HEDGING) { ulong ticket=PositionGetTicket(index); if(ticket==0) return(false); } else { string name=PositionGetSymbol(index); if(name=="") return(false); } //--- return(true); } //+------------------------------------------------------------------+
交易策略 2
价格脉冲(Price Impulse) 的概念用于实施基于第二种测量价格速度的交易策略。它的主要观点是,当价格移动一定数量的点时,考虑到为此花费的计时周期的数量,打开交易头寸。
参数 | 描述 |
---|---|
时间框架 | 任意 |
买入条件 | 在某个分时数内超过了价格点数 |
卖出条件 | 在某个分时数内超过了价格点数 |
退场条件 | 获利/止损 |
该策略的实现方法在下面列出,我们可以看到, 这些设置有两个参数负责价格速度估计:
//+------------------------------------------------------------------+ //| EA 的输入参数 | //+------------------------------------------------------------------+ input string InpEaComment = "Strategy #2"; // EA Comment input int InpMagicNum = 1111; // Magic number input double InpLots = 0.1; // Lots input uint InpStopLoss = 400; // StopLoss in points input uint InpTakeProfit = 500; // TakeProfit in points input ENUM_COPY_TICKS tick_flags = TICKS_INFO; // Ticks resulting from Bid and/or Ask changes input int InpPoints = 15; // The price should move NNN points input uchar InpTicks = 15; // For XXX ticks //--- 取得分时的数组 MqlTick tick_array_curr[]; // 用于当前分时的分时数组 MqlTick tick_array_prev[]; // 用于之前分时的分时数组 ulong tick_from=0; // 如果 tick_from=0, 给出最新的 tick_count 个分时 uint tick_count=15; // 应当取得的分时数量 //--- double ExtStopLoss=0.0; double ExtTakeProfit=0.0; double ExtPoints=0.0; bool first_start=false; long last_trade_time=0; //+------------------------------------------------------------------+ //| EA 交易初始化函数 | //+------------------------------------------------------------------+ int OnInit() { if(!m_symbol.Name(Symbol())) // 设置交易品种名称 return(INIT_FAILED); tick_count+=InpTicks; // 请求 "tick_count" + "for XXX ticks" ExtStopLoss=InpStopLoss*Point(); ExtTakeProfit=InpTakeProfit*Point(); ExtPoints=InpPoints*Point(); first_start=false; //--- 请求 ticks (第一次填充) int copied=CopyTicks(Symbol(),tick_array_curr,tick_flags,tick_from,tick_count); if(copied!=tick_count) first_start=false; else { first_start=true; ArrayCopy(tick_array_prev,tick_array_curr); } m_trade.SetExpertMagicNumber(InpMagicNum); m_trade.SetTypeFillingBySymbol(Symbol()); m_trade.SetMarginMode(); m_trade.LogLevel(LOG_LEVEL_NO); //--- return(INIT_SUCCEEDED); } //+------------------------------------------------------------------+ //| EA交易分时函数 | //+------------------------------------------------------------------+ void OnTick() { //--- 检查第一次开始 int copied=-1; if(!first_start) { copied=CopyTicks(Symbol(),tick_array_curr,tick_flags,tick_from,tick_count); if(copied!=tick_count) first_start=false; else { first_start=true; ArrayCopy(tick_array_prev,tick_array_curr); } } //--- 请求分时 copied=CopyTicks(Symbol(),tick_array_curr,tick_flags,tick_from,tick_count); if(copied!=tick_count) return; int index_new=-1; long last_time_msc=tick_array_prev[tick_count-1].time_msc; for(int i=(int)tick_count-1;i>=0;i--) { if(last_time_msc==tick_array_curr[i].time_msc) { index_new=i; break; } } //--- if(index_new!=-1 && !IsOpenedByMagic(InpMagicNum)) { int shift=(int)tick_count-1-index_new-InpTicks; // 当前分时数组的偏移 shift=(shift<0)?0:shift; if(tick_array_curr[tick_count-1].ask-tick_array_curr[shift].ask>ExtPoints) { //--- 开启 BUY double sl=(InpStopLoss==0)?0.0:tick_array_curr[tick_count-1].ask-ExtStopLoss; double tp=(InpTakeProfit==0)?0.0:tick_array_curr[tick_count-1].ask+ExtTakeProfit; m_trade.Buy(InpLots,m_symbol.Name(),tick_array_curr[tick_count-1].ask, m_symbol.NormalizePrice(sl), m_symbol.NormalizePrice(tp)); last_trade_time=tick_array_curr[tick_count-1].time_msc; } else if(tick_array_curr[shift].bid-tick_array_curr[tick_count-1].bid>ExtPoints) { //--- 开启 SELL double sl=(InpStopLoss==0)?0.0:tick_array_curr[tick_count-1].bid-ExtStopLoss; double tp=(InpTakeProfit==0)?0.0:tick_array_curr[tick_count-1].bid+ExtTakeProfit; m_trade.Sell(InpLots,m_symbol.Name(),tick_array_curr[tick_count-1].bid, m_symbol.NormalizePrice(sl), m_symbol.NormalizePrice(tp)); last_trade_time=tick_array_curr[tick_count-1].time_msc; } } ArrayCopy(tick_array_prev,tick_array_curr); //--- } //+------------------------------------------------------------------+ //| 使用幻数检查开启的仓位 | //+------------------------------------------------------------------+ bool IsOpenedByMagic(int MagicNumber) { int pos=0; uint total=PositionsTotal(); //--- for(uint i=0; i<total; i++) { if(SelectByIndex(i)) if(PositionGetInteger(POSITION_MAGIC)==MagicNumber) pos++; } return((pos>0)?true:false); } //+------------------------------------------------------------------+ //| 根据索引选择仓位 | //+------------------------------------------------------------------+ bool SelectByIndex(const int index) { ENUM_ACCOUNT_MARGIN_MODE margin_mode=(ENUM_ACCOUNT_MARGIN_MODE)AccountInfoInteger(ACCOUNT_MARGIN_MODE); //--- if(margin_mode==ACCOUNT_MARGIN_MODE_RETAIL_HEDGING) { ulong ticket=PositionGetTicket(index); if(ticket==0) return(false); } else { string name=PositionGetSymbol(index); if(name=="") return(false); } //--- return(true); } //+------------------------------------------------------------------+
交易策略 3
为了创建一个间接测量价格速度的交易策略,我们应该选择一个趋势指标和一个确定其可能信号的过滤器。考虑到目标,我为趋势指标选择了Trend Direction and Force(趋势方向和力度指标),并选择了HMA Color作为过滤器。图7 显示了买入和卖出的入场信号。
图 7. 交易策略的入场条件
参数 | 描述 |
---|---|
使用的指标 | 趋势方向和力度 |
使用的指标 | HMA Color |
时间框架 | M30 以及更高 |
买入条件 | 'Trend direction and fast' 值高于阈值 (线为蓝色), 而 HMA 向上变蓝. |
卖出条件 | 'Trend direction and fast' 值低于阈值 (线为红色), 而 HMA 向下变红. |
退场条件 | 获利/止损 |
测试价格速度测量方法
要测试所选的三种策略,我们需要定义它们要测试的条件。
- 时间段: 近一年. (在写这篇文章时, 这是 01.01.2019 — 26.05.2019)
- 交易品种: EURUSD.
- 交易模式: 没有延迟 (这些不是高频交易策略, 所以延迟的影响将非常小).
- 测试: М1 OHLC (在真实分时上的预先测试显示了近似相同的结果).
- 初始存款: 1000 USD.
- 杠杆: 1:500.
- 服务器: MetaQuotes-Demo.
- 报价: 5-digit.
测试的目的不是根据历史来调整策略,而是澄清各种计算和处理价格速度的方法的总体趋势和效率。因此,将为每个交易策略提供最佳的优化结果及其使用报告参数的估计。
交易策略 1.
在测试了基于标准价格速度变化的第一种策略后,我得到了一定的结果。其中前20名如下:
图 8. 交易策略1 20个最佳优化结果
根据这些结果,我们可以得出结论,在这些测试条件下,交易策略在低时间段内表现出最佳的结果,具有较小的获利和止损值。换句话说,被检测信号的变化潜力很小。同时,夏普比率(有关夏普比率的更多信息和其它参数,请参阅文章交易中的数学,如何评估交易结果) 较低,这不是一个可靠的标志。接下来,我在测试器中启动了最佳结果,并收到了以下报告。
图 9. 交易策略测试结果1最佳优化结果
当使用常数0.1时,我们可以看到增长率几乎为100%,而夏普比率仅为0.33,而z值为-0.16,这表明交易缺乏一致性和随机因素的影响。
交易策略 2.
为了测试以下策略,我们需要根据策略特性更改一个测试条件:
- 测试: 把 M1 OHLC (在真实分时中的预先测试显示几乎相同的结果) 替换为 "基于真实分时的每一分时"。
该策略的前20个优化选项如下:
图10. 交易策略2 20个最佳优化结果
平均而言,与第一种策略相比,这里的夏普比率更高,并且在最小节拍数范围内适度的点数可以获得最佳结果。结果表明,该系统的选择性也很强,因为交易数量非常少。让我们测试最佳优化结果并查看报告。
图 11. 交易策略测试结果2最佳优化结果
夏普比率在这里看起来更好,因为超过30%的利润只在10个交易中达到。
交易策略 3.
在这个策略中,我们回到初始的测试条件。在这里,我们将使用间接决定价格变动及其速度的指标来检查交易。
另请参阅优化当前策略的最佳选项:
图 12. 交易策略3 20个最佳优化结果
大多数结果都非常相似,这表明,最后两个优化参数对最终结果的影响不大。同时,平均夏普比约为0.2,这并不太令人印象深刻。当然,最佳的优化结果是无序的,但我们将像以前的交易策略一样对其进行回溯测试。
图13. 测试交易策略3的最佳优化结果
尽管存款几乎100%的增长和出色的夏普比率(值得注意的是,它花了52笔交易才达到当前的利润水平,而最接近的结果几乎少了两倍),Z评分显示交易之间呈负相关。这意味着有利可图的交易很可能紧随亏损交易,反之亦然。
总结
在测试了三种价格速度测量方法和最佳后验和其他优化方法的结果后,发现第一种方法的最佳优化结果缺乏一致性。其余两种方法均显示出较好的结果,夏普比率较高,且交易之间呈负相关。当然,测试策略并没有涵盖各种各样的价格速度测量方法,但本文描述了一些最明显和最容易理解的交易方法。
结论
下面所附的档案包含所有描述的文件,这些文件排列在适当的文件夹中。要执行正确的操作,应将MQL5文件夹保存到终端的根目录。要打开终端根目录,其中MQL5文件夹所在,请在MetaTrader 5终端中按Ctrl+Shift+D按键组合,或使用如下图14所示的上下文菜单。
图 14. 打开 MetaTrader 5 终端根目录中的 MQL5 文件夹
本文使用的程序:
# |
名称 |
类型 |
描述 |
---|---|---|---|
1 |
Strategy_1.mq5 | EA | 基于交易策略1的EA交易 |
2 | Strategy_2.mq5 | EA | 基于交易策略2的EA交易 |
3 | Strategy_3.mq5 | EA | 基于交易策略3的EA交易 |
4 | Engine.mqh | 库 | 交易函数库 |
5 | 平均速度 | 指标 | 在交易策略1中使用 |
6 | CAM | 指标 | 在交易策略1中使用 |
7 | 趋势方向和力度 | 指标 | 在交易策略3中使用 |
8 | HMA_color | 指标 | 在交易策略3中使用 |