模拟存取款
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 处理程序中测试TesterWithdrawal 和 TesterDeposit 函数的性能。具体来说,试图提取双倍余额会导致错误 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(_Symbol, SYMBOL_VOLUME_MIN) : Volume;
ENUM_POSITION_PROPERTY_DOUBLE props[] = {POSITION_PROFIT, POSITION_SWAP};
double values[][2];
ulong tickets[];
PositionFilter pf;
pf.select(props, tickets, values, true);
...
|
没有仓位时,我们可在预先定义的方向打开一个仓位。
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 = 0, swaps = 0;
for(int i = 0; i < 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 交易报告
因此,在一年多一点的时间里,当交易一个最小手数并存入不超过 1% 的保证金时,我们设法提取了约 200 美元。