模拟存取款

MetaTrader 5 测试程序允许你模拟存取款操作。你可以体验一些资金管理系统的操作。

bool TesterDeposit(double money)

TesterDeposit 函数用于在测试资金参数中的存款金额过程中补充账户。金额以测试存款货币表示。

bool TesterWithdrawal(double money)

TesterWithdrawal 函数可使提款等于 money

两个函数均返回 true 来表示成功。

例如,我们考虑一个基于“利差交易”策略的 EA 交易。对于该交易,我们需要在其中一个交易方向上选择一个有较大正掉期的符号,例如,买入 AUDUSD。EA 交易会在指定方向开立一个或多个仓位。无盈利仓位将被持有,以便积累掉期交易。当每手交易达到预定利润时,盈利仓位将被平仓。赚取的掉期可从账户中提取。CrazyCarryTrade.mq5 文件中提供了源代码。

在输入参数中,用户可以选择交易方向、交易规模(默认为 0,表示最小手数)以及每手的最小利润,在该点将平仓止盈的仓位。

enum ENUM_ORDER_TYPE_MARKET
{
   MARKET_BUY = ORDER_TYPE_BUY,
   MARKET_SELL = ORDER_TYPE_SELL
};
   
input ENUM_ORDER_TYPE_MARKET Type;
input double Volume;
input double MinProfitPerLot = 1000;

首先,我们在 OnInit 处理程序中测试TesterWithdrawalTesterDeposit 函数的性能。具体来说,试图提取双倍余额会导致错误 10019。

int OnInit()
{
   PRTF(TesterWithdrawal(AccountInfoDouble(ACCOUNT_BALANCE) * 2));
   /*
   not enough money for 20 000.00 withdrawal (free margin: 10 000.00)
   TesterWithdrawal(AccountInfoDouble(ACCOUNT_BALANCE)*2)=false / MQL_ERROR::10019(10019)
   */
   ...

但是随后的 100 单位账户货币的提款和贷记将会成功。

   PRTF(TesterWithdrawal(100));
   /*
   deal #2 balance -100.00 [withdrawal] done
   TesterWithdrawal(100)=true / ok
   */
   PRTF(TesterDeposit(100)); // return the money 
   /*
   deal #3 balance 100.00 [deposit] done
   TesterDeposit(100)=true / ok
   */
   return INIT_SUCCEEDED;
}

OnTick 处理程序中,我们使用 PositionFilter 检查仓位的可用性,并用其当前的利润/损失和累计掉期来填充 values 数组。

void OnTick()
{
   const double volume = Volume == 0 ?
      SymbolInfoDouble(_SymbolSYMBOL_VOLUME_MIN) : Volume;
   ENUM_POSITION_PROPERTY_DOUBLE props[] = {POSITION_PROFITPOSITION_SWAP};
   double values[][2];
   ulong tickets[];
   PositionFilter pf;
   pf.select(propsticketsvaluestrue);
   ...

没有仓位时,我们可在预先定义的方向打开一个仓位。

   if(ArraySize(tickets) == 0// no positions 
   {
      MqlTradeRequestSync request1;
      (Type == MARKET_BUY ? request1.buy(volume) : request1.sell(volume));
   }
   else
   {
      ... // there are positions - see the next box
   }

有仓位时,我们在一个周期内进行处理,并平仓那些有足够利润的仓位(根据掉期调整)。如此操作的同时,我们也可以总结平仓和总损失的掉期。由于掉期与时间成比例增长,我们可用其作为平仓“旧”仓位的放大因子。因此,有可能以亏损平仓。

      double loss = 0swaps = 0;
      for(int i = 0i < ArraySize(tickets); ++i)
      {
         if(values[i][0] + values[i][1] * values[i][1] >= MinProfitPerLot * volume)
         {
            MqlTradeRequestSync request0;
            if(request0.close(tickets[i]) && request0.completed())
            {
               swaps += values[i][1];
            }
         }
         else
         {
            loss += values[i][0];
         }
      }
      ...

如果总损失增加,我们会定期开设额外的仓位,但当有更多的仓位时,我们会减少开设仓位的频率,以便以某种方式控制风险。

      if(loss / ArraySize(tickets) <= -MinProfitPerLot * volume * sqrt(ArraySize(tickets)))
      {
         MqlTradeRequestSync request1;
         (Type == MARKET_BUY ? request1.buy(volume) : request1.sell(volume));
      }
      ...

最后,我们从账户中移除掉期。

      if(swaps >= 0)
      {
         TesterWithdrawal(swaps);
      }

OnDeinit 处理程序中,显示了关于扣除的统计数据。

void OnDeinit(const int)
{
   PrintFormat("Deposit: %.2f Withdrawals: %.2f",
      TesterStatistics(STAT_INITIAL_DEPOSIT),
      TesterStatistics(STAT_WITHDRAWAL));
}

例如,在 2021 年至 2022 年初期间使用默认设置运行 EA 交易时,我们会得到以下 AUDUSD 结果:

   final balance 10091.19 USD
   Deposit: 10000.00 Withdrawals: 197.42

报告和图表如下所示。

账户提款的 EA 交易报告

账户提款的 EA 交易报告

因此,在一年多一点的时间里,当交易一个最小手数并存入不超过 1% 的保证金时,我们设法提取了约 200 美元。