这里 https://www.earnforex.com/metatrader-expert-advisors/expert-advisor-template/ 有一个 EA 编程模板,注释也很全,定义了相关状态的枚举,没有使用魔数。
OnTick 函数:
//The OnTick function is triggered every time MT4 receives a price change for the symbol in the chart
void OnTick(){
//Re-initialize the values of the global variables at every run
InitializeVariables();
//ScanOrders scans all the open orders and collect statistics, if an error occurs it skips to the next price change
if(!ScanOrders()) return;
//CheckNewBar checks if the price change happened at the start of a new bar
CheckNewBar();
//CheckOperationHours checks if the current time is in the operating hours
CheckOperationHours();
//CheckSpread checks if the spread is above the maximum spread allowed
CheckSpread();
//CheckTradedThisBar checks if there was already a trade executed in the current candle
CheckTradedThisBar();
//EvaluateExit contains the code to decide if there is an exit signal
EvaluateExit();
//ExecuteExit executes the exit in case there is an exit signal
ExecuteExit();
//Scan orders again in case some where closed, if an error occurs it skips to the next price change
if(!ScanOrders()) return;
//Execute Trailing Stop
ExecuteTrailingStop();
//EvaluateEntry contains the code to decide if there is an entry signal
EvaluateEntry();
//ExecuteEntry executes the entry in case there is an entry signal
ExecuteEntry();
}
- 首先,重新初始化全局变量的值。
- 然后,扫描所有已开立的订单并收集统计数据。如果出现错误,跳过到下一个价格变动。
- 检查价格变动是否发生在新的K线(蜡烛)开始时。
- 检查当前时间是否在交易操作时间范围内。
- 检查当前的点差是否超过允许的最大点差。
- 检查当前K线内是否已经执行了交易。
- 根据特定的条件判断是否存在退出信号。
- 如果存在退出信号,执行退出操作。
- 再次扫描订单,以防有订单被关闭。如果出现错误,跳过到下一个价格变动。
- 执行追踪止损操作。
- 根据特定的条件判断是否存在入场信号。
- 如果存在入场信号,执行入场操作。
EvaluateEntry 函数
//Evaluate if there is an entry signal
void EvaluateEntry(){
SignalEntry=SIGNAL_ENTRY_NEUTRAL;
//if(!IsSpreadOK) return; //If the spread is too high don't give an entry signal
//if(UseTradingHours && !IsOperatingHours) return; //If you are using trading hours and it's not a trading hour don't give an entry signal
//if(!IsNewCandle) return; //If you want to provide a signal only if it's a new candle opening
//if(IsTradedThisBar) return; //If you don't want to execute multiple trades in the same bar
//if(TotalOpenOrders>0) return; //If there are already open orders and you don't want to open more
//This is where you should insert your Entry Signal for BUY orders
//Include a condition to open a buy order, the condition will have to set SignalEntry=SIGNAL_ENTRY_BUY
//This is where you should insert your Entry Signal for SELL orders
//Include a condition to open a sell order, the condition will have to set SignalEntry=SIGNAL_ENTRY_SELL
}
EvaluateEntry
函数:此函数用于判断是否存在入场信号。- 函数内部的逻辑:
- 首先,将
SignalEntry
设置为中性(SIGNAL_ENTRY_NEUTRAL
)。 - 然后,根据一系列条件判断是否应该提供入场信号:
- 如果点差过高,不提供入场信号。
- 如果使用交易时间限制且当前不在交易时间内,不提供入场信号。
- 如果不是新的K线(蜡烛)开盘,不提供入场信号。
- 如果当前K线内已经执行了交易,不提供入场信号。
- 如果已经存在其他开放订单且不希望再开立更多订单,不提供入场信号。
- 在注释中,作者提醒您在以下位置插入具体的入场信号逻辑:
- 对于买入订单,设置
SignalEntry=SIGNAL_ENTRY_BUY
。 - 对于卖出订单,设置
SignalEntry=SIGNAL_ENTRY_SELL
。
- 对于买入订单,设置
- 首先,将
LotsOptimized 函数
double LotsOptimized()
{
if (!MM) return (Lots);
double Size, RiskMoney, PositionSize = 0;
if (AccountCurrency() == "") return(0);
if (FixedBalance > 0)
{
Size = FixedBalance;
}
else if (UseEquityInsteadOfBalance)
{
Size = AccountEquity();
}
else
{
Size = AccountBalance();
}
if (!UseMoneyInsteadOfPercentage) RiskMoney = Size * Risk / 100;
else RiskMoney = MoneyRisk;
double UnitCost = MarketInfo(Symbol(), MODE_TICKVALUE);
double TickSize = MarketInfo(Symbol(), MODE_TICKSIZE);
if ((StopLoss != 0) && (UnitCost != 0) && (TickSize != 0)) PositionSize = NormalizeDouble(RiskMoney / (StopLoss * UnitCost / TickSize), LotDigits);
if (PositionSize < MarketInfo(Symbol(), MODE_MINLOT)) PositionSize = MarketInfo(Symbol(), MODE_MINLOT);
else if (PositionSize > MarketInfo(Symbol(), MODE_MAXLOT)) PositionSize = MarketInfo(Symbol(), MODE_MAXLOT);
return(PositionSize);
}
- 首先,这是一个函数定义,名为
LotsOptimized
。 - 函数内部的逻辑如下:
- 如果不使用资金管理(MM),则直接返回已设定的交易手数(
Lots
)。 - 否则,根据不同情况计算交易手数:
- 如果账户货币为空,返回0。
- 如果设置了固定余额(
FixedBalance
),则使用该余额。 - 否则,如果选择使用权益而不是余额(
UseEquityInsteadOfBalance
),则使用账户权益(AccountEquity()
)。 - 否则,使用账户余额(
AccountBalance()
)。
- 根据风险管理设置计算风险金额(
RiskMoney
):- 如果不使用资金百分比而是直接使用金额(
UseMoneyInsteadOfPercentage
),则使用预设的风险金额(MoneyRisk
)。 - 否则,根据账户大小和风险百分比计算风险金额。
- 如果不使用资金百分比而是直接使用金额(
- 获取交易品种的单价(
UnitCost
)和最小价格变动(TickSize
)。 - 如果设置了止损(
StopLoss
)且单价和价格变动不为零,则计算交易手数(PositionSize
):- 根据风险金额、止损点数和价格变动计算交易手数。
- 确保交易手数不低于最小手数(
MODE_MINLOT
)且不高于最大手数(MODE_MAXLOT
)。
- 返回计算得到的交易手数。
- 如果不使用资金管理(MM),则直接返回已设定的交易手数(