如果您在交易入场和离场时不使用限价或定损订单,那么您用的就是市价订单,当然,您得到的最终价格则会基于这些订单买卖点差的大小来判定。
当您激发买入按钮时,您实际上以卖家的要价买入,其点差可能比您决定买入时的买家出价高一点。
当您激发卖出按钮时,您实际上以买家的出价卖出,其点差价低于卖家的要价。
当然,在您激发平仓按钮了结您之前做多的持仓时,您实际上以当前的买家出价卖出。
反之亦然,当您激发平仓按钮了结之前做空的持仓时,您实际上以当前卖家的要价回购或回补。
现在我们可以利用来自 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