分享

网格马丁策略真的能躺赚?看完这篇万字长文你就知道了

 禁忌石 2023-07-24 发布于浙江

导言

在这篇文章中,我打算使用数学和编程来深入研究这些策略,并评估它们的盈利能力。文章包括数学和实践部分。在数学部分,我将提供计算策略预期回报和其他许多交易者忽视的重要参数的方程式。在实践部分,我将开发一个简单的网格和马丁格尔系统,并将方程式与实际情况进行比较。本文对于初学者来说尤其有用,因为这些策略通常是他们接触的第一种。对它们盲目信任可能会导致失望和浪费时间,就像我曾经经历的那样。如果我当时不懂数学,也许我还会相信它们。但是如果你正确理解它们,这些策略仍然是合理的。这就是我要证明的。

这两种策略有什么共同之处?

要理解这两种策略的受欢迎程度,我们应该看一下所有新手外汇交易者所希望的东西。大多数新手交易者都是数学家和梦想家,他们认为自己的智慧可以帮助他们迅速轻松地致富。我曾经也是这样的梦想家。这两种策略都给人以有利可图的交易形象,即策略回测中的图表不断上升。

在大部分历史时间区间,即使没有任何过滤器,测试器也可能显示类似圣杯的结果。在任何活动领域和任何行业中,都存在着更加了解情况的人能够以你的代价获得财富的危险。外汇交易也不例外。

外汇市场存在许多这样的欺骗性做法,而这两种策略是最具说明性和普遍流行的证据。当你第一次使用这些策略时,你会发现它们在所有货币对上都能产生令人难以置信的利润因子和预期回报,并且甚至可以应对任何点差,所以这个算法似乎超越了市场。

这是因为它们是基于纯数学而没有逻辑的。即使经过这么多年,我仍然希望找到让我无论价格方向如何都能始终获利的算法。数学家通常是有趣的人,他们能够凭借正确的方程式证明任何事情,无论这在现实中是否真实。))总的来说,这两种策略利用了保持盈亏平衡的幻觉来说服你使用它们。

它们都适用于任何货币对和任何周期,或者更准确地说,创造了工作的假象以说服你它们的简单性和效率。深入研究这些策略,你迟早会意识到自己什么也不知道。然而,这个阶段是必要的,因为这是开始理性思考并了解市场的真实本质以及你真正需要使用哪些策略的唯一方法。

网格及其基本公式

订单网格的目的是在任何市场中获利。无论市场是下跌还是上涨,如果市场有明显的趋势,那么根据这个想法,网格会使用一个聪明的订单交易系统来买入订单,以便这些订单最终在某个点上获得足够的利润来一次性平仓它们。让我在下面的图片中展示这一点:

网格马丁策略真的能躺赚?看完这篇万字长文你就知道了

这里我展示了两种选项,分别针对上涨和下跌的市场。根据网格策略,无论我们选择哪种选项,我们都应该能够赢利。使用网格的人总是建议使用挂单,因为它们会在最佳价格触发。这是对的,但我认为市价单也不错,因为你能够控制入场时的点差和滑点。此外,你还可以稍微延迟入场。然而,在这个策略中,限价单更好。

我们有一个起始点,相对于这个点,我们放置订单。在这个点之上,我们设置买单的步长为's',而在这个点之下,我们设置卖单。如果价格达到他们,它们将转化为市价订单。图片显示了基于具体定价情况的开放订单。在这里展示限价单没有意义,因为它们在上涨和下跌时保持在同一水平上。对我们来说,只有开放的实际订单才重要,因为它们的利润或亏损会累加到总的利润或亏损中。为了确保利润,某些订单应该比其他订单大“K”倍,即我们应该提供K=a/d,其中K>=K0。达到K0时,网格订单的总利润超过零阈值。

在MetaTrader 4中,我们也可以以同样简单的方式计算仓位或订单的当前利润。否则,我们将出现这样一种情况:价格立即朝某个方向移动,而我们要在价格上涨或下跌“n”个点之后才能获利。严格来说,这个比率是可以计算出来的,但也可以手动选择。可能的计算如下所示:

Nl=d/s - 1

NP=(a+d)/s -1

Pr=Sum(1,NP)(s*i)=(s+s*NP)*Np/2

Ls=Sum(1,Nl)(s*j+s*NP)=(s+s*Nl)*Nl/2+s*NP^2

总结所有订单的损益,我们可以看到这些总和是等差数列。有一个方程描述了等差数列的总和(使用它的首项和末项),在这里得到应用。

考虑到Pr-Ls=0,解决这个方程可以得到'a',从而可以计算出K0=a/d。此外,使用这些方程可以确定具有开放买入和卖出仓位的交易周期的利润因子和预期收益。

Prf=Pr/Ls

M=(Pr-Ls)/(Np+Nl)

这些方程计算的是特定交易周期的利润因子和预期回报,而不是整个图表。只要我们的图表结束于循环的终点,利润因子就会是正数。一个循环就是一个独立的网格。网格被构建,并且被充分利用,仓位被关闭,然后又构建新的网格。这是对于无限存款来说的一个无限过程。在资金曲线上,大致看起来是这样的:

网格马丁策略真的能躺赚?看完这篇万字长文你就知道了

这里我提供了一个简化的网格机器人资金曲线图,以展示历史运行情况。资金能够承受几个循环,并且曲线上升。但是这最终会以资金不足的循环结束,我们所有可见的利润都会被券商拿走。从数学角度来看,这被视为一个未完成的循环。未完成的循环总是亏损的,它的损失会抵消掉所有完成的循环中获得的利润。循环也可能因为所需订单数量不足而无法继续构建网格。所有券商都对终端或特定货币对的同时开放订单数量设置了限制。网格不能无限地构建下去。即使我们假设我们能够做到,最终我们仍然会得到上述结果。

文章末尾,我将简要解释为什么从数学角度而言会发生这种情况。

马丁格尔及其基本公式

与网格一样,马丁格尔的思想是无论市场走势如何都能获胜。它基于永久盈利的幻觉。如果我们开仓后盈利,我们继续交易。一旦亏损,我们将下一笔订单的手数相对于亏损部位增加 'n' 倍。如果我们的订单盈利,我们简单地平仓,并将手数重置为初始值。如果订单再次亏损,重复上一步操作,同时将手数相对于循环中所有亏损部位的总手数增加 'n' 倍。持续重复直到再次获得盈利交易。循环中的最后一笔交易总是盈利的,其利润总是能够弥补亏损交易的损失。这就是资金曲线开始形成循环的方式。

假设资金曲线以最后一个循环结束,我们会得到正向期望和盈利因子。开仓的方式和位置并不重要,最好这些订单具有固定的盈利和止损,或者在固定的止损水平处平仓。以下是马丁格尔机器人资金曲线的示例:

网格马丁策略真的能躺赚?看完这篇万字长文你就知道了

从图中可以看出,马丁格尔资金曲线与网格相似,因为马丁格尔也是按循环运行的。唯一的区别在于,马丁格尔始终只开一笔订单,并等待该订单关闭后再开下一笔。和网格一样,迟早会出现资金不足以完成循环的情况,所有订单都被关闭,资金被清零。为了确保盈利的循环,最后一笔交易的利润应该能够弥补之前交易的亏损:

Nl

Np=1

Pr=L[Nl+Np]*TP[Nl+Np]*TickSize

Ls=Sum(1,Nl)(L[i]*SL[i])*TickSize

在这里,利润是以您的账户货币单位而非点数计算的,因为系统涉及到手数大小。特定订单的手数是通过递归计算得出的:

L[1]=StartLot

for(2,Nl) L[i]=(K*Sum(1,i-1)(L[j]*SL[j]))/TP[i]

其中,“K”是循环所需的盈利因子。这里并未考虑点差、佣金和掉期,但我认为这不重要。如果有需要,方程可以很容易地进行修改,尽管我不清楚修改的意义所在。马丁格尔的方程与网格方程相似。SL和TP分别代表订单的损失和期望盈利。通过解决以下简单方程,我们可以得到定义:K=(L[i]* TP[i])/Sum(1,i-1)(L[j]*SL[j])。

开发和测试最简单的网格EA(外汇交易机器人)

使用MQL5语言开发和测试网格EA和简单的马丁格尔策略,以测试它们并查看结果。我将从网格开始。首先,在我们的模板中添加一些方便处理持仓的类:

#include <Trade\PositionInfo.mqh>

#include <Trade\Trade.mqh>

CPositionInfo m_position=CPositionInfo();// trade position object

CTrade m_trade=CTrade(); // trading object

这两个库在MetaTrader 5中始终默认存在,因此不会出现编译问题。

接下来,让我们描述所有必要的输入参数:

///grid variables

input int MaxChannelSizePoints=500;//Max Of a+d

input int MinMoveToClose=100;//Mininum Move

input int GridStepPoints=20;//Grid Step In Points

input int BarsI=999;//Bars To Start Calculate

input double KClose=3.5;//Asymmetry

///

////////minimum trading implementation

input int SlippageMaxOpen=15; //Slippage For Open In Points

input double Lot=0.01;//Lot

input int MagicC=679034;//Magic

/////////

第一个模块实现了所有必要的网格参数,而第二个模块以最简单的形式实现了固定手数交易的功能。

在启动EA时,我们需要验证并从上一个会话中恢复网格参数,以防操作因意外终止。这个功能是可选的,但最好事先实现这些内容:

void DimensionAllMQL5Values()//////////////////////////////

{

ArrayResize(Time,BarsI,0);

ArrayResize(High,BarsI,0);

ArrayResize(Low,BarsI,0);

}

void CalcAllMQL5Values()///////////////////////////////////

{

ArraySetAsSeries(High,false);

ArraySetAsSeries(Low,false);

ArraySetAsSeries(Time,false);

CopyHigh(_Symbol,_Period,0,BarsI,High);

CopyLow(_Symbol,_Period,0,BarsI,Low);

CopyTime(_Symbol,_Period,0,BarsI,Time);

ArraySetAsSeries(High,true);

ArraySetAsSeries(Low,true);

ArraySetAsSeries(Time,true);

}

这段代码需要实现预定义数组以完成初始分析。之后,我们将不再需要这些数组。我们只在初始计算过程中使用它们。

恢复操作的执行方式如下:

void RestoreGrid()//recover the grid if the robot is restarted

{

DimensionAllMQL5Values();

CalcAllMQL5Values();

bool ord=PositionSelect(Symbol());

if ( ord && int(PositionGetInteger(POSITION_MAGIC)) == MagicC )

{

GridStartTime=datetime(PositionGetInteger(POSITION_TIME));

GridStartPrice=double(PositionGetDouble(POSITION_PRICE_OPEN));

GridUpPrice=GridStartPrice;

GridDownPrice=GridStartPrice;

for(int i=0;i<BarsI;i++)

{

if ( High[i] > GridUpPrice ) GridUpPrice=High[i];

if ( Low[i] < GridDownPrice ) GridDownPrice=Low[i];

if ( Time[i] < GridStartTime ) break;

}

bCanUpdate=true;

bTryedAlready=false;

}

}

为了跟踪当前网格状态,我们需要额外的变量来显示网格存在期间的上限和下限价格,以及网格的起始价格和设置时间。

bool bCanUpdate;//whether it is possible to update the grid

bool bTryedAlready;//whether there was an attempt to close a position

在开发过程中创建和更新网格参数的步骤如下:

void CreateNewGrid()//create a new grid

{

SymbolInfoTick(Symbol(),LastTick);

GridStartTime=TimeCurrent();

GridStartPrice=LastTick.bid;

GridUpPrice=GridStartPrice;

GridDownPrice=GridStartPrice;

double SummUp=LastTick.ask+double(GridStepPoints)*_Point;

double SummDown=LastTick.bid-double(GridStepPoints)*_Point;

while ( SummUp <= LastTick.ask+double(MaxChannelSizePoints)*_Point )

{

m_trade.BuyStop(Lot,SummUp,Symbol());

SummUp+=double(GridStepPoints)*_Point;

}

while ( SummDown >= LastTick.bid-double(MaxChannelSizePoints)*_Point )

{

m_trade.SellStop(Lot,SummDown,Symbol());

SummDown-=double(GridStepPoints)*_Point;

}

}

void UpdateGrid()//update the grid parameters

{

SymbolInfoTick(Symbol(),LastTick);

if ( LastTick.bid > GridUpPrice ) GridUpPrice=LastTick.bid;

if ( LastTick.bid < GridDownPrice ) GridDownPrice=LastTick.bid;

}

用于平仓和清理剩余限价单的函数,以及检测关闭网格条件的谓词函数如下所示:

void ClosePosition()//close a position by a symbol

{

bool ord;

ord=PositionSelect(Symbol());

if ( ord && int(PositionGetInteger(POSITION_MAGIC)) == MagicC )

{

if(m_position.SelectByIndex(0)) m_trade.PositionClose(m_position.Ticket());

}

}

void CleanLimitOrders()//clear limit orders

{

int orders=OrdersTotal();

for(int i=0;i<orders;i++)

{

ulong ticket=OrderGetTicket(i);

if(ticket!=0)

{

m_trade.OrderDelete(ticket);

}

}

}

bool bCanClose()//closure condition

{

if ( GridStartPrice == GridUpPrice && (GridStartPrice-GridDownPrice)/_Point >= MinMoveToClose ) return true;

if ( GridStartPrice == GridDownPrice && (GridUpPrice-GridStartPrice)/_Point >= MinMoveToClose ) return true;

if ( GridStartPrice != GridUpPrice && GridStartPrice != GridDownPrice

&& (GridStartPrice-GridDownPrice)/(GridUpPrice-GridStartPrice) >= KClose

&& (GridStartPrice-GridDownPrice)/_Point >= MinMoveToClose ) return true;

if ( GridStartPrice != GridDownPrice && GridStartPrice != GridUpPrice

&& (GridUpPrice-GridStartPrice)/(GridStartPrice-GridDownPrice) >= KClose

&& (GridUpPrice-GridStartPrice)/_Point >= MinMoveToClose ) return true;

/*

if ( GridUpPrice >= GridStartPrice+MaxChannelSizePoints*_Point

//|| GridDownPrice <= GridStartPrice-MaxChannelSizePoints*_Point ) return true;

*/

return false;

}

我已经将谓词函数中的最后一个条件注释掉了。它在价格移出网格时关闭网格。您可以根据需要使用它,它不会对其他东西产生影响。现在我们只需要编写主要的交易函数了:

void Trade()//the main function where all actions are performed

{

bool ord=PositionSelect(Symbol());

if ( bCanUpdate ) UpdateGrid();

if ( ord && bCanClose() )//if there is a position and the closing condition is met

{

ClosePosition();

CleanLimitOrders();

bCanUpdate=false;

bTryedAlready=true;

}

if ( bTryedAlready ) ClosePosition();

if ( !bCanUpdate && !ord )

{

CleanLimitOrders();

CreateNewGrid();

bCanUpdate=true;

bTryedAlready=false;

}

}

此外,让我们定义在初始化EA时何时调用以及要调用什么和要执行什么操作:

int OnInit()

{

m_trade.SetExpertMagicNumber(MagicC);//set the magic number for positions

RestoreGrid();//restore the grid if present

return(INIT_SUCCEEDED);

}

void OnTick()

{

Trade();

}

我们已经开发了网格EA。现在让我们对其进行测试,看看它的表现如何:

网格马丁策略真的能躺赚?看完这篇万字长文你就知道了

正如您所见,关于亏损循环的假设已得到确认。一开始,网格表现得相当不错,但随后会出现网格不足的情况,导致亏损循环摧毁了所有的利润。在市场趋势明显的时段,通常能够取得良好的结果,而在盘整时段则更容易出现亏损。由于仍然存在点差,因此总体结果始终是亏损。

开发和测试最简单的马丁格尔EA

既然我们已经处理了网格,那么让我们继续开发马丁格尔EA。其代码将会简单得多。为了处理持仓,我们将使用在网格EA中应用的库。没有必要再次显示代码。让我们直接考虑输入参数:

input int SLE=100;//Stop Loss Points

input int TPE=300;//Take Profit Points

input int SlippageMaxOpen=15; //Slippage For Open In Points

input double Lot=0.01;//Start Lot

input int MagicC=679034;//Magic

input int HistoryDaysLoadI=10;//History Deals Window Days

为了更简单起见,我选择了一种系统,即仅通过止损或止盈来关闭持仓。最后一个变量允许我们避免不断加载整个订单历史记录,而只加载必要的窗口(纯粹为了优化)。我相信其他变量是不言自明的。

该EA只包含两个函数:

double CalcLot()//calculate the lot

{

bool ord;

double TotalLot=0;

HistorySelect(TimeCurrent()-HistoryDaysLoadI*86400,TimeCurrent());

for ( int i=HistoryDealsTotal()-1; i>=0; i-- )

{

ulong ticket=HistoryDealGetTicket(i);

ord=HistoryDealSelect(ticket);

if ( ord && HistoryDealGetString(ticket,DEAL_SYMBOL) == _Symbol

&& HistoryDealGetInteger(ticket,DEAL_MAGIC) == MagicC

&& HistoryDealGetInteger(ticket,DEAL_ENTRY) == DEAL_ENTRY_OUT )

{

if ( HistoryDealGetDouble(ticket,DEAL_PROFIT) < 0 )

{

TotalLot+=HistoryDealGetDouble(ticket,DEAL_VOLUME);

}

else

{

break;

}

}

}

return TotalLot == 0 ? Lot: TotalLot;

}

void Trade()//the main function where all actions are performed

{

bool ord=PositionSelect(Symbol());

SymbolInfoTick(Symbol(),LastTick);

if ( !ord )

{

if ( MathRand() > 32767.0/2.0 )

{

m_trade.Buy(CalcLot(),_Symbol,LastTick.ask,LastTick.bid-double(SLE)*_Point,LastTick.ask+double(TPE)*_Point);

}

else

{

m_trade.Sell(CalcLot(),_Symbol,LastTick.ask,LastTick.ask+double(SLE)*_Point,LastTick.bid-double(TPE)*_Point);

}

}

}

第一个函数是用于在查看交易历史后计算开仓时使用的最终手数。如果最后一笔交易亏损,下一手的手数将等于之前所有亏损交易手数的总和,直到第一笔盈利交易为止。如果最后一笔交易盈利,将手数重置为初始值。在主要函数中,我们以不同的方向和随机的固定止损水平开仓,而手数则使用第一个函数进行计算。为了使所有这些正常工作,我们需要在初始化器中为EA分配一个魔术数,并且主要函数与网格EA一样在OnTick处理程序中调用。

这完成了最简单马丁格尔的开发。现在让我们对其进行测试并查看结果:

网格马丁策略真的能躺赚?看完这篇万字长文你就知道了

案例类似于网格交易。我们可以看到其中的循环。在亏损周期开始之前,加仓EA起作用。保证金不足以开立下一个仓位,从而发生亏损。就像网格一样,它在某些地方奏效,在某些地方不奏效,但最终总是以亏损告终。既然我们已经考虑了这两种策略,现在是时候得出数学结论,这些结论比仅仅理解这两种策略更重要。

网格和加仓背后的共同数学原理

为什么我认为网格和加仓背后的共同数学原理如此重要?如果我们彻底理解它,我们最终可以告别一系列永远不会给我们带来利润的想法,尽管我们可能希望相信它们。至少,我们将了解哪些条件可以促成这些策略的表现。此外,我们还将意识到为什么纯粹的加仓和网格是输钱的策略。

让我们想象任何策略由无限数量的最简单策略组成。当订单开启时,其中之一将被激活。我们假设这些订单以固定的亏损或盈利平仓。让我们将它们与数组C[i]和Lot[i]对应起来,其中这些数组的大小相等且趋近于无穷大。假设每个策略应用的手数总是不同。此外,让我们引入任何这些策略被触发的概率PC[i],当然这些事件构成一个完整的组,使得Sum(0,n)( PC[i] ) = 1。这些事件的所有结果形成新的事件空间S[i]、T[i],分别表示亏损和盈利的激活。这些事件具有自己的条件概率PS[i]、PT[i],当然也构成一个完整的组。下面是它们的图形表示:

网格马丁策略真的能躺赚?看完这篇万字长文你就知道了

现在让我们考虑列表中的任何一种策略,并计算其预期收益。

M[i]=(PT[i]*TP-PS[i]*SL)*Lot[i]*TickSize.

如果我们不知道仓位开启点的价格走势,我们可以说M[i]=0,其中M[i]是特定策略的预期收益数组。换句话说,如果我们不知道价格走向,无论我们如何交易,只要交易次数趋近于无穷大,我们会得到0。

一般的预期收益方程如下:

M0=Sum(0,n)(PC[i]*M[i])

我们知道当n趋向于无穷大时,所有的M[i]都趋向于零,这意味着如果策略的数量是有限的,而交易的数量是无限的,我们求和的所有项都趋向于0。这反过来意味着总的预期收益M0仍然等于0。进一步思考后,我们发现一个无限的有限策略集合也等于零,因为对无限个零进行求和的结果是0。在网格的情况下,每个位置的手数都是相同的,而在加仓的情况下,手数是不同的,但这种差异并不影响最终的预期收益。这两种策略都可以使用这个一般方程来描述,甚至不需要考虑组合数学。一切都非常简单明了。

由于这些策略可以用这个方程来描述,那么对于任何策略都适用。这导致一个结论,即所有涉及变化和操纵交易量的策略,以及任何复杂度的开仓/平仓系统,在没有知道开仓和平仓时价格走势或至少一些辅助市场参数的正确预测的情况下注定会失败。没有正确的预测,我们的所有努力只是时间和金钱的浪费。

如何正确使用网格和加仓法

在某种程度上,如果你知道即将出现的市场走势或这种情况的概率很高,网格可能会有所帮助,但同时也存在着跳空的风险。跳空和网格并不是很搭配。这是因为订单是按照一定步骤下达的,可能会发生下一个价格变动超过所有订单,超出网格范围的情况。

当然,这种情况很少见,但它不可避免地降低了系统的性能。网格大小应该设置为预测移动的相等或略小的值,我们不需要知道移动的方向,只需要知道其大致数值。下面是在成功的趋势检测算法下的收益曲线示例:

网格马丁策略真的能躺赚?看完这篇万字长文你就知道了

并不总是有可能事先确定趋势。这通常导致损失阶段(上面标为红色的部分)并且回撤也相当大。对于那些能够检测到大幅波动的方法的人来说,网格可能是有用的。在这种情况下,网格可以基于自定义信号。我还没有涉及到这个问题,所以如果有人对检测到强势波动有好的算法,请随时分享你的经验。

现在让我们来考虑加仓法。如果我们有任何预期收益为“0”的信号,但已知连续若干次亏损的概率接近于一,即连续若干次亏损后有盈利交易的概率接近于一,那么可以使用该信号进行加仓。收益曲线如下图所示:

网格马丁策略真的能躺赚?看完这篇万字长文你就知道了

但我个人认为,无论在哪种情况下,加仓法都是危险的。我认为几乎不可能实现我所描述的那种条件,尽管对于网格来说,一切似乎更容易理解,而且最重要的是更清晰。

结论

在本文中,我试图尽可能清楚地描述这两种策略,并突出它们的相似之处、优点和缺点。我相信,即使对于初学者来说,所有的解释都很容易理解。该文章主要面向新手交易者,但所得出的结论比仅仅对策略和应用范围进行简单评估要重要得多。这里提供的一般数学结论可以帮助每个人在开发自己的交易系统时更高效地利用自己的时间。本文本身没有带来对市场规律的新认识,但我认为它可以防止许多交易者盲目地利用这些原则来谋取自己的利益。

®本文由著名俄罗斯数学家 Evgeniy Ilin写成,由本人翻译整理。请勿随意转载

最后的最后,小编对Evgeniy Ilin的看法是认同的,网格交易和马丁策略可以作为我们的交易系统重要的部分,极大的减小了我们的风险和判断市场趋势付出的成本。

    本站是提供个人知识管理的网络存储空间,所有内容均由用户发布,不代表本站观点。请注意甄别内容中的联系方式、诱导购买等信息,谨防诈骗。如发现有害或侵权内容,请点击一键举报。
    转藏 分享 献花(0

    0条评论

    发表

    请遵守用户 评论公约

    类似文章 更多