MetaTrader 5 中的出价/要价(Bid/Ask)点差分析

文章讨论了交易中的点差问题,指出市价订单的实际成本通常高于经纪商宣称的平均点差,特别是在波动市场中。通过MetaTrader5的历史数据,作者展示了点差的真实情况,强调这对交易策略,特别是剥头皮和风险管理的影响。文章还提供了一个OnInit()和OnCalculate()代码示例,用于计算和比较实际点差与宣称点差。
摘要由CSDN通过智能技术生成

如果您在交易入场和离场时不使用限价或定损订单,那么您用的就是市价订单,当然,您得到的最终价格则会基于这些订单买卖点差的大小来判定。

当您激发买入按钮时,您实际上以卖家的要价买入,其点差可能比您决定买入时的买家出价高一点。

当您激发卖出按钮时,您实际上以买家的出价卖出,其点差价低于卖家的要价。

当然,在您激发平仓按钮了结您之前做多的持仓时,您实际上以当前的买家出价卖出。

反之亦然,当您激发平仓按钮了结之前做空的持仓时,您实际上以当前卖家的要价回购或回补。

现在我们可以利用来自 MetaTrader 5 的即时报价数据来分析近期历史得真实平均买卖点差是多少。

您不需要查看当前点差,因为若您同时显示出价和要价指示线时,该值已出示。

我们来看看这是为什么以及如何应对

查看这些图表,您可以看到该经纪商平台的点差大部分为 5 个点。

如果是这种情况,那么您在交易得开始和结束往返过程中的成本应该是 1 个点。

故此,对于具有 1/1 回报风险比率、10 点止损和 10 点止盈的交易,您的成本应该为 10% 的风险/投注。

这样的点差足够公平,例如博彩公司的过盈率通常为 15%,赌场的利润率约为 4%。

但实际的平均点差(红线)与经纪商平台记录的点差(黑色虚线)相比,大多是下方数据窗口内所确认的官方宣称点差的两倍。 在早期示例中拥有相同 SL 和 TP,您的成本通常至少为 2 个点或 20%。 

若您运用小尺度的剥头皮,即,用 5 个点的 SL 和 TP,或者若您决定如前面例子触发 10 个点 SL 或 TP 即离场,或说亏损 5 个点,那么成本同样是 2 个点,但因为交易后行情开始对您不利,出于安全,百分比成本现在是您投注/风险的 40%。 

当我还是萌新交易者时,我一开始用 5 个点 S/l 和 10 个点 T/P 来实现 2:1 的风险/回报比(我怀疑许多萌新交易者都是如此做)。 我并未成功。

因此,我针对 EURUSD M1 图表利用一款可靠的之字指标进行了深入分析。 我将腿长设置为最小 5 个点,对于我来说,这是足以下手的回撤。

结果似乎表明,大多数小幅波动都在 7 个点左右,相比之下,10 个点的腿长相对较小。 当然,我考虑到新闻发布和波动行情,因此结果主要来自交易时段的平均周期。

因此,我选用 10 个点止损,并保留止盈为空,如此我就可以密切监控交易,且若交易盈亏已达 7 个点,则决定交易何时离场。 成果有一些改进,但只给我留了一点蝇头小利。 直到此刻我才注意到与该经纪商对赌时超高的买卖点差,由此转而寻找更好的经纪商。

如果您在新闻发布或行情波动时进行交易,您会看到实际平均点差上升到 15 个点左右,或标准 5 个点的 3 倍,如此您必须支付 3 点或 60% 的投注。 

甚至不考虑在英国时间 20:30(图表服务器时间为 21:30)之后交易,那时很可能是 4、5、6 倍甚至更高,尤其是如果您决定持仓过周末,正如您在下面看到的,这几乎是标准 5 个点点差的 10 倍,除非您的止损和止盈价位留有非常大的空间。

OnInit() 代码示例

#property indicator_separate_window

#property indicator_buffers 2
#property indicator_plots   2

//--- plots
#property indicator_label1  "ActSpread"
#property indicator_type1   DRAW_LINE
#property indicator_color1  clrRed
#property indicator_style1  STYLE_SOLID
#property indicator_width1  2

#property indicator_label2  "DeclaredSpread"
#property indicator_type2   DRAW_LINE
#property indicator_color2  clrBlack
#property indicator_style2  STYLE_DASH
#property indicator_width2  2

//--- indicator parameters
input int      numRecentBarsBack=100; //#RecentBarsBack M30+~100, M5~200, M1~500
input bool     doPrint=true;          //true=prints to the toolbox\experts log

//--- indicator buffers
double         ActSpreadBuf[], DeclaredSpreadBuf[];

//+------------------------------------------------------------------+
//| Custom indicator initialization function                         |
//+------------------------------------------------------------------+
int OnInit()  
{
   int numBars=iBars(_Symbol,PERIOD_CURRENT)-2; 
   
   // Check we have enough data for the request before we begin
   if(numRecentBarsBack>numBars) 
   { 
      Alert("Can't Do ", numRecentBarsBack, "! Only ",  
               numBars, " Bars are Available", 
               " try 100 or so for 30+ minute charts,",
               " 200 for 5 minute, or 500 for 1 minute charts.",
               " Otherwise the indicator may be too slow"
           ); 
           
      return(INIT_PARAMETERS_INCORRECT);
   }

   double sumPrice=0; 
   double avgPrice=0; 

   // Get the standard 5 point spread for the standard EURUSD currency
   double stdSpread=0.00005/iClose("EURUSD",PERIOD_M1,1); // 1.2 ~=  EURUSD std price
   
   //Find out the current average price of the instrument we are using, so we can standardise the spread and _Point
   int CheckAvgPriceBars=MathMin(numRecentBarsBack, 200);
   
   int i=0;
   for(; i<CheckAvgPriceBars; i++)
   {
      sumPrice+=iClose(_Symbol,PERIOD_CURRENT,i);
   }
   avgPrice=sumPrice/(i? i: 1.0);
   
   //convert the stdSpread to stdPoint by dividing by 5, so we compare  apples with apples, not oranges
   double stdPoint=StringToDouble(DoubleToString(avgPrice*stdSpread/5.0,6));

   Print(i, "=bars done, avgPrice=", DoubleToString(avgPrice,6), 
            " std=", DoubleToString(1.2*stdSpread, 6), 
            " stdPoint=", DoubleToString(stdPoint, 6)
         );
   
   SetIndexBuffer(0,ActSpreadBuf,INDICATOR_DATA);         
   SetIndexBuffer(1,DeclaredSpreadBuf,INDICATOR_DATA);    
   
   string indName ="BAS("+_Symbol;
          indName+=" TF="+string(_Period);
          indName+=" stdPoint="+DoubleToString(stdPoint, 6);
          indName+=") Last("+string(numRecentBarsBack)+") Bars";
          
   IndicatorSetString(INDICATOR_SHORTNAME, indName); 
   
   IndicatorSetInteger(INDICATOR_DIGITS,6); 
   
   IndicatorSetDouble(INDICATOR_MINIMUM, 0.0); 

   IndicatorSetInteger(INDICATOR_LEVELS, 20);     
   
   //mark out each standard EURUSD 5 point spread, to compare this currencies spread with EURUSD
   IndicatorSetDouble(INDICATOR_LEVELVALUE,0,  0.000000); 
   IndicatorSetDouble(INDICATOR_LEVELVALUE,1,  5*stdPoint); 
   IndicatorSetDouble(INDICATOR_LEVELVALUE,2, 10*stdPoint); 
   IndicatorSetDouble(INDICATOR_LEVELVALUE,3, 15*stdPoint); 
   IndicatorSetDouble(INDICATOR_LEVELVALUE,4, 20*stdPoint); 
   IndicatorSetDouble(INDICATOR_LEVELVALUE,5, 25*stdPoint); 
   IndicatorSetDouble(INDICELVALUE,17,85*stdPoint); 
   IndicatorSetDouble(INDICATOR_LEVELVALUE,18,90*stdPoint); 
   IndicatorSetDouble(INDICATOR_LEVELVALUE,19,95*stdPoint); 
    
return(INIT_SUCCEEDED);
}

对于这个简单的 2 个绘图笔指标,只有 2 个参数,第一个 'numRecentBarsBack' 是我们想要分析的柱线数。

我们在 OnInit() 中做的第一件任务就是检查我们是否有足够的数据来满足请求,如果我们还没有,那么我们会提醒用户,并建议采用一些可靠值,然后以错误码提早退出指标。

OnInit() 的其余部分是相当标准的,除了指标子窗口中使用的水平,这些水平设置为对应于标准 EURUSD 5 点差的倍数的值。 

这是一个相当重要的步骤,因为除了要查看所声明值与实际平均点差值之间的比较结果,我们还想查看不同货币的点差与标准 EURUSD 相比的差距,通常其会伴随所有货币的最低点差。

这是一个相当复杂的方法,因为我们必须获得当前 EURUSD 价格(如果不存在则用 1.2 替代),并采用 5 个 EURUSD 点除以该价格来构建标准点差。 然后我们遍历当前外汇金融产品的 numRecentBarsBack 价格(我没有选用非外汇金融产品进行测试)以获得该金融产品的平均价格。

当我们拥有了金融产品的平均价格时,我们然后将金融产品的平均价格乘以先前建立的标准点差,并除以 5,即标准 EURUSD 点差来构建一个四舍五入的标准点差。

这个四舍五入的标准点差之后会用在每个价位值,也包含在指标短名称中,如下面 “exotic” USDMXN 图表中的指标名称所示。

在该 USDMXN 示例中,日间交易宣称的点差约为 0.0025,比零点高约 3 个点差水平,因此对应于 EURUSD 图表中的约 15 个点。 另请注意,实际平均点差甚至高于该经纪商的高位。

下面的 GBPAUD 图表示意,日间交易宣称的点差约为 0.00019,比零高约 2.5 个点差水平,因此对应于 EURUSD 图表中的约 12 个点。 另请注意,在该图表中,实际平均点差值非常接近该经纪商宣称的数值。

下面的 GBPJPY 图表示意,日间交易宣称的点差约为 0.020,比零高约 3 个点差水平,因此对应于 EURUSD 图表中的约 15 个点。 另请注意,在该图表中,实际平均点差值非常接近该经纪商宣称的数值。

下面的 USDJPY 图表示意,日间交易宣称的点差约为 0.0050,比零高约 1 个点差水平,因此对应于 EURUSD 图表中的约 5 个点。 另请注意,在此图表中,实际平均点差值再次大致为宣称值的两倍,因此针对 EURUSD 风险/回报百分比水平的评论也同样适用于此处。

此处还有几个例子,您可自行对点差水平之间的关系进行评估。

第二个参数是布尔值 'doPrint',它会在代码中被检查,如果为真,则将各根柱线的统计数据打印到智能系统日志,如下面的示例所示。 如果 'numRecentBarsBack' 的值太大,这会减慢指标的速度,因此默认值为 100。

如果您把 'doPrint' 参数设置为 true,并将 'numRecentBarsBack' 设置为一个合理的值,例如 30 分钟图表的 100,或 1 分钟图表的 300,那么您可以复制日志记录,并将它们发送给您的经纪商作为其平台提供的真实买卖点差的证据。

OnCalculate() 代码示例

//--- Global variables
//--- Set the date formatting for printing to the log
const uint dtFormat=uint(TIME_DATE|TIME_MINUTES); 

//+------------------------------------------------------------------+
//| 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[])
{
   //--- Check for no data or Stop flag before we begin               
   if(_StopFlag || rates_total<2)  
   { 
         Alert("Error, StopFlag=", _StopFlag, " #Bars=", rates_total);    
         return(rates_total);    
   }
   
   //only do the report at indicator start up or refresh 
   if(prev_calculated>2) 
   {         
      // if we have already nulled the ActSpreadBuf just do the DeclaredSpreadBuf[] and return.
      if(prev_calculated==rates_total)
      {
         int currBar=rates_total-1;
         DeclaredSpreadBuf[currBar]=spread
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值