Summary: 本文全面讲解MQL4语言中的函数定义与调用,包括函数声明、定义、参数传递、返回值、作用域和重载,通过实际EA代码示例学习创建模块化的EA程序。




一、为什么函数在EA开发中如此重要

函数是执行特定任务的可重用代码块。它们是模块化编程的基石,让你能够将EA代码组织成逻辑清晰、易于维护和可测试的单元。没有函数,EA代码将变得庞大臃肿,难以调试,几乎无法维护。

二、函数完整速查表

| 组成部分 | 语法格式 | 用途说明 | 代码示例 |
|----------|----------|----------|----------|
| 函数声明 | 返回类型 函数名(参数); | 告知编译器函数存在 | double CalculateLot(double risk); |
| 函数定义 | 返回类型 函数名(参数) { 代码 } | 定义函数的具体行为 | double CalculateLot(double r) { return r*100; } |
| 参数 | 类型 参数名 | 向函数传递数据 | double riskPercent |
| 返回值 | return 值; | 从函数返回数据 | return result; |
| 函数调用 | 函数名(参数值); | 执行函数 | double lot = CalculateLot(2); |

三、函数基础 - 声明、定义和调用

```mql4
// 函数声明(原型)- 告知编译器函数存在
double CalculateLotSize(double riskPercent, double stopLossPoints);
bool IsTradingHours();
string GetOrderTypeName(int orderType);

// 函数定义 - 具体实现代码
double CalculateLotSize(double riskPercent, double stopLossPoints) {
double accountBalance = AccountBalance();
double riskAmount = accountBalance * riskPercent / 100;
double pointValue = MarketInfo(Symbol(), MODE_TICKVALUE);
double lotSize = riskAmount / (stopLossPoints * pointValue);

// 按允许步长规范化
double stepSize = MarketInfo(Symbol(), MODE_LOTSTEP);
double minLot = MarketInfo(Symbol(), MODE_MINLOT);
double maxLot = MarketInfo(Symbol(), MODE_MAXLOT);

lotSize = MathFloor(lotSize / stepSize) * stepSize;
lotSize = MathMax(minLot, MathMin(maxLot, lotSize));

return NormalizeDouble(lotSize, 2);
}

// 函数调用 - 使用函数
void OnTick() {
double lot = CalculateLotSize(2.0, 50.0);
Print("计算出的手数:", lot);
}
```

四、函数返回类型详解

```mql4
// int返回类型 - 返回整数
int CountOpenOrders(int magic) {
int count = 0;
for(int i = 0; i < OrdersTotal(); i++) {
if(OrderSelect(i, SELECT_BY_POS, MODE_TRADES)) {
if(OrderMagicNumber() == magic && OrderSymbol() == Symbol()) {
count++;
}
}
}
return count;
}

// double返回类型 - 返回小数
double CalculateAveragePrice() {
double totalPrice = 0;
int count = 0;
for(int i = 0; i < OrdersTotal(); i++) {
if(OrderSelect(i, SELECT_BY_POS, MODE_TRADES)) {
if(OrderMagicNumber() == magicNumber && OrderSymbol() == Symbol()) {
totalPrice += OrderOpenPrice();
count++;
}
}
}
if(count == 0) return 0;
return totalPrice / count;
}

// bool返回类型 - 返回真/假
bool IsTradeAllowed() {
if(!IsTradeAllowed()) return false;
if(CountOpenOrders(magicNumber) >= MAX_ORDERS) return false;
double spread = MarketInfo(Symbol(), MODE_SPREAD);
if(spread > MAX_SPREAD) return false;
return true;
}

// string返回类型 - 返回文本
string GetSignalDescription(int signalType) {
switch(signalType) {
case SIGNAL_STRONG_BUY: return "强买入 - 多指标一致";
case SIGNAL_BUY: return "买入 - 趋势确认";
case SIGNAL_STRONG_SELL: return "强卖出 - 多指标一致";
case SIGNAL_SELL: return "卖出 - 趋势确认";
default: return "无信号";
}
}

// void返回类型 - 无返回值
void LogMessage(string message) {
string timestamp = TimeToString(TimeCurrent(), TIME_DATE|TIME_MINUTES);
Print("[", timestamp, "] ", message);
}
```

五、函数参数 - 向函数传递数据

```mql4
// 单个参数
double CalculatePips(double priceDifference) {
return priceDifference / Point();
}

// 多个参数
double CalculatePositionSize(double riskPercent, double stopLossPoints, double accountBalance) {
double riskAmount = accountBalance * riskPercent / 100;
double pointValue = MarketInfo(Symbol(), MODE_TICKVALUE);
return riskAmount / (stopLossPoints * pointValue);
}

// 默认参数(必须从右向左设置)
double CalculateLotWithDefault(double riskPercent, double stopLossPoints = 50.0) {
return CalculatePositionSize(riskPercent, stopLossPoints, AccountBalance());
}

// 引用传递(使用&)- 可修改原始变量
void CalculateRiskMetrics(double &drawdown, double &profitFactor) {
double peakBalance = AccountBalance();
double currentBalance = AccountBalance();
drawdown = (peakBalance - currentBalance) / peakBalance * 100;
profitFactor = 1.5; // 简化计算
}

// 数组参数
double CalculateArrayAverage(double &arr[], int size) {
if(size <= 0) return 0;
double sum = 0;
for(int i = 0; i < size; i++) {
sum += arr[i];
}
return sum / size;
}

// 多参数类型的完整示例
bool ExecuteTrade(int orderType, double lotSize, double stopLossPoints, double takeProfitPoints, string &errorMessage) {
double price = (orderType == OP_BUY) ? Ask : Bid;
double sl = 0, tp = 0;

if(stopLossPoints > 0) {
sl = (orderType == OP_BUY) ? price - stopLossPoints * Point() : price + stopLossPoints * Point();
}
if(takeProfitPoints > 0) {
tp = (orderType == OP_BUY) ? price + takeProfitPoints * Point() : price - takeProfitPoints * Point();
}

int ticket = OrderSend(Symbol(), orderType, lotSize, price, 30, sl, tp, "交易", magicNumber, 0, clrNONE);

if(ticket < 0) {
errorMessage = "OrderSend失败,错误码:" + IntegerToString(GetLastError());
return false;
}
return true;
}
```

六、函数重载 - 同名不同参

```mql4
// 重载函数:CalculateLot - 不同的参数组合
double CalculateLot(double riskPercent) {
// 使用默认止损50点
return CalculateLot(riskPercent, 50.0);
}

double CalculateLot(double riskPercent, double stopLossPoints) {
// 使用当前账户余额
return CalculateLot(riskPercent, stopLossPoints, AccountBalance());
}

double CalculateLot(double riskPercent, double stopLossPoints, double accountBalance) {
double riskAmount = accountBalance * riskPercent / 100;
double pointValue = MarketInfo(Symbol(), MODE_TICKVALUE);
double lotSize = riskAmount / (stopLossPoints * pointValue);

double stepSize = MarketInfo(Symbol(), MODE_LOTSTEP);
double minLot = MarketInfo(Symbol(), MODE_MINLOT);
double maxLot = MarketInfo(Symbol(), MODE_MAXLOT);

lotSize = MathFloor(lotSize / stepSize) * stepSize;
lotSize = MathMax(minLot, MathMin(maxLot, lotSize));

return NormalizeDouble(lotSize, 2);
}

// 使用示例
void OnTick() {
double lot1 = CalculateLot(2.0); // 使用默认止损
double lot2 = CalculateLot(2.0, 60.0); // 指定止损
double lot3 = CalculateLot(2.0, 60.0, 50000.0); // 指定所有参数
}
```

七、函数中的作用域

```mql4
// 全局变量 - 任何地方都可访问
double g_globalVariable = 100;
int g_tradeCount = 0;

// 包含局部变量的函数
void ProcessTrade() {
// 局部变量 - 仅在此函数内部可访问
double localVariable = 50;
int localCounter = 0;

localCounter++;
g_tradeCount++; // 可访问和修改全局变量

Print("局部变量:", localVariable, " 全局变量:", g_globalVariable);
// 函数退出时localVariable被销毁
}

// 静态变量 - 在函数调用之间保持数值
int GetNextTicketID() {
static int nextID = 1000; // 仅初始化一次,保持数值
nextID++;
return nextID;
}

void DemonstrateStatic() {
for(int i = 0; i < 5; i++) {
Print("票据号:", GetNextTicketID()); // 返回1001,1002,1003,1004,1005
}
}
```

八、实用EA函数库

```mql4
// ============ 订单管理函数 ============

// 按类型和魔术号统计订单数量
int CountOrders(int magic, int orderType = -1) {
int count = 0;
for(int i = 0; i < OrdersTotal(); i++) {
if(OrderSelect(i, SELECT_BY_POS, MODE_TRADES)) {
if(OrderSymbol() == Symbol() && OrderMagicNumber() == magic) {
if(orderType == -1 || OrderType() == orderType) {
count++;
}
}
}
}
return count;
}

// 计算所有持仓的总盈利
double CalculateTotalProfit(int magic) {
double totalProfit = 0;
for(int i = 0; i < OrdersTotal(); i++) {
if(OrderSelect(i, SELECT_BY_POS, MODE_TRADES)) {
if(OrderSymbol() == Symbol() && OrderMagicNumber() == magic) {
totalProfit += OrderProfit() + OrderSwap() + OrderCommission();
}
}
}
return totalProfit;
}

// 关闭所有持仓
int CloseAllPositions(int magic) {
int closed = 0;
for(int i = OrdersTotal() - 1; i >= 0; i--) {
if(OrderSelect(i, SELECT_BY_POS, MODE_TRADES)) {
if(OrderSymbol() == Symbol() && OrderMagicNumber() == magic) {
double closePrice = (OrderType() == OP_BUY) ? Bid : Ask;
if(OrderClose(OrderTicket(), OrderLots(), closePrice, 30, clrNONE)) {
closed++;
}
}
}
}
return closed;
}

// ============ 风险管理函数 ============

// 基于风险百分比计算仓位大小
double CalculateRiskPosition(double riskPercent, double stopLossPoints) {
double accountEquity = AccountBalance();
double riskDollars = accountEquity * riskPercent / 100;
double tickValue = MarketInfo(Symbol(), MODE_TICKVALUE);
double rawLots = riskDollars / (stopLossPoints * tickValue);

double minLot = MarketInfo(Symbol(), MODE_MINLOT);
double maxLot = MarketInfo(Symbol(), MODE_MAXLOT);
double stepSize = MarketInfo(Symbol(), MODE_LOTSTEP);

rawLots = MathFloor(rawLots / stepSize) * stepSize;
rawLots = MathMax(minLot, MathMin(maxLot, rawLots));

return NormalizeDouble(rawLots, 2);
}

// 检查是否达到每日亏损限额
bool IsDailyLossLimitReached(double maxLossPercent) {
static double startBalance = 0;
static datetime lastReset = 0;

datetime currentDay = GetMidnight();
if(currentDay != lastReset) {
startBalance = AccountBalance();
lastReset = currentDay;
}

double currentBalance = AccountBalance();
double lossPercent = (startBalance - currentBalance) / startBalance * 100;

return lossPercent >= maxLossPercent;
}

// ============ 指标函数 ============

// 检查移动平均线交叉
int CheckMACrossover(int fastPeriod, int slowPeriod) {
double maFastCurrent = iMA(NULL, 0, fastPeriod, 0, MODE_SMA, PRICE_CLOSE, 1);
double maSlowCurrent = iMA(NULL, 0, slowPeriod, 0, MODE_SMA, PRICE_CLOSE, 1);
double maFastPrevious = iMA(NULL, 0, fastPeriod, 0, MODE_SMA, PRICE_CLOSE, 2);
double maSlowPrevious = iMA(NULL, 0, slowPeriod, 0, MODE_SMA, PRICE_CLOSE, 2);

if(maFastCurrent > maSlowCurrent && maFastPrevious <= maSlowPrevious) {
return SIGNAL_BUY; // 金叉
}
else if(maFastCurrent < maSlowCurrent && maFastPrevious >= maSlowPrevious) {
return SIGNAL_SELL; // 死叉
}
return SIGNAL_NONE;
}

// 检查RSI背离
int CheckRSIDivergence(int rsiPeriod) {
double rsiCurrent = iRSI(NULL, 0, rsiPeriod, PRICE_CLOSE, 1);
double rsiPrevious = iRSI(NULL, 0, rsiPeriod, PRICE_CLOSE, 2);
double priceCurrent = iClose(NULL, 0, 1);
double pricePrevious = iClose(NULL, 0, 2);

// 底背离:价格创新低,RSI未创新低
if(priceCurrent < pricePrevious && rsiCurrent > rsiPrevious) {
return SIGNAL_BUY;
}
// 顶背离:价格创新高,RSI未创新高
else if(priceCurrent > pricePrevious && rsiCurrent < rsiPrevious) {
return SIGNAL_SELL;
}
return SIGNAL_NONE;
}

// ============ 时间管理函数 ============

// 检查当前时间是否在交易时段内
bool IsTradingTime(int startHour, int endHour) {
MqlDateTime timeStruct;
TimeToStruct(TimeCurrent(), timeStruct);
int hour = timeStruct.hour;
int dayOfWeek = timeStruct.day_of_week;

// 跳过周末(周六=6,周日=0)
if(dayOfWeek == 0 || dayOfWeek == 6) return false;

return (hour >= startHour && hour < endHour);
}

// 获取下一根K线开盘时间
datetime GetNextBarOpen(int timeframe) {
datetime currentTime = TimeCurrent();
datetime barTime = iTime(Symbol(), timeframe, 0);
return barTime + PeriodSeconds(timeframe);
}

// ============ 工具函数 ============

// 规范化价格到经纪商小数位
double NormalizePrice(double price) {
int digits = (int)MarketInfo(Symbol(), MODE_DIGITS);
return NormalizeDouble(price, digits);
}

// 格式化数字显示
string FormatNumber(double number, int decimals) {
return DoubleToString(number, decimals);
}

// 带频率限制的发送警报
void SendAlert(string message, int cooldownSeconds = 60) {
static datetime lastAlert = 0;
if(TimeCurrent() - lastAlert >= cooldownSeconds) {
Alert(message);
lastAlert = TimeCurrent();
}
}
```

九、完整模块化EA(使用所有函数特性)

```mql4
//+------------------------------------------------------------------+
//| ModularEA.mq4 |
//+------------------------------------------------------------------+
#property copyright "Copyright 2024"
#property version "1.00"
#property strict

// 输入参数
input double InpRiskPercent = 2.0; // 每笔交易风险%
input int InpMagic = 12345; // EA魔术号
input int InpMaxSpread = 30; // 允许的最大点差
input int InpTrailingStop = 30; // 移动止损点数
input int InpStartHour = 8; // 交易开始小时
input int InpEndHour = 17; // 交易结束小时
input double InpMaxDailyLoss = 5.0; // 最大每日亏损%

// 信号常量
#define SIGNAL_NONE 0
#define SIGNAL_BUY 1
#define SIGNAL_SELL 2

// 全局变量
datetime g_lastBarTime = 0;
int g_dailyTradeCount = 0;

//+------------------------------------------------------------------+
//| EA初始化函数 |
//+------------------------------------------------------------------+
int OnInit() {
LogMessage("EA初始化成功");
LogMessage("每笔交易风险:" + DoubleToString(InpRiskPercent, 1) + "%");
LogMessage("魔术号:" + IntegerToString(InpMagic));
return(INIT_SUCCEEDED);
}

//+------------------------------------------------------------------+
//| EA主执行函数 |
//+------------------------------------------------------------------+
void OnTick() {
// 新K线检测
if(Time[0] == g_lastBarTime) return;
g_lastBarTime = Time[0];

// 交易前验证
if(!IsTradeAllowed()) {
Comment("交易未允许");
return;
}

if(!IsTradingTime(InpStartHour, InpEndHour)) {
Comment("非交易时段");
return;
}

if(IsDailyLossLimitReached(InpMaxDailyLoss)) {
Comment("每日亏损限额已达");
return;
}

// 持仓管理
if(CountOrders(InpMagic) > 0) {
ManageAllPositions();
return;
}

// 信号生成和执行
int signal = GenerateTradeSignal();
if(signal != SIGNAL_NONE) {
ExecuteTrade(signal);
}
}

//+------------------------------------------------------------------+
//| 使用多个指标生成交易信号 |
//+------------------------------------------------------------------+
int GenerateTradeSignal() {
double maFast = iMA(NULL, 0, 10, 0, MODE_SMA, PRICE_CLOSE, 1);
double maSlow = iMA(NULL, 0, 30, 0, MODE_SMA, PRICE_CLOSE, 1);
double rsi = iRSI(NULL, 0, 14, PRICE_CLOSE, 1);
double spread = MarketInfo(Symbol(), MODE_SPREAD);

// 点差检查
if(spread > InpMaxSpread) {
LogMessage("点差过高:" + DoubleToString(spread, 1));
return SIGNAL_NONE;
}

// 信号逻辑
if(maFast > maSlow && rsi < 30) {
LogMessage("生成买入信号 - 均线看涨 + RSI超卖");
return SIGNAL_BUY;
}
else if(maFast < maSlow && rsi > 70) {
LogMessage("生成卖出信号 - 均线看跌 + RSI超买");
return SIGNAL_SELL;
}

return SIGNAL_NONE;
}
```

十、函数最佳实践清单

  • [ ] 使用描述性的函数名称,能够表明函数用途

  • [ ] 保持函数小而专注,每个函数只做一件事

  • [ ] 在文件顶部使用函数声明(原型)

  • [ ] 在函数开头验证所有输入参数

  • [ ] 对大数据结构使用const或引用传递

  • [ ] 用注释记录复杂函数的作用

  • [ ] 尽量避免在函数内部修改全局变量

  • [ ] 使用函数内部的静态变量保持状态

  • [ ] 返回有意义的错误码或使用引用参数返回错误信息

  • [ ] 在集成到EA之前独立测试每个函数


  • 参考来源:

  • MetaQuotes Ltd.《MQL4官方文档 - 函数》(2024)

  • McConnell, Steve.《代码大全:软件构建实践手册》(2004)

  • 孙伟.《MQL4模块化编程实战》(2023)


  • 9. 下一步

    第11篇将讲解MQL4订单管理函数(OrderSend、OrderModify、OrderSelect) – 开仓、修改和关闭订单的完整指南,附带实际示例和错误处理。