一、为什么账户与市场信息函数至关重要
账户与市场信息函数是专业EA风险管理的基础。没有这些函数,你的EA无法评估可用资金、计算合适的仓位大小、遵守经纪商限制或适应不断变化的市场条件。掌握这些函数能将你的EA从一个简单的信号跟随者转变为一个稳健的风险管理交易系统。
二、账户与市场函数完整速查表
| 类别 | 函数名 | 返回值类型 | 主要用途 |
|------|--------|------------|----------|
| 账户 | AccountBalance() | double | 当前账户余额 |
| 账户 | AccountEquity() | double | 当前账户净值(余额+浮动盈亏) |
| 账户 | AccountFreeMargin() | double | 可用于交易的可用保证金 |
| 账户 | AccountMargin() | double | 持仓占用的保证金 |
| 账户 | AccountLeverage() | int | 账户杠杆(如100:1) |
| 账户 | AccountProfit() | double | 总浮动盈亏 |
| 账户 | AccountName() | string | 账户持有人名称 |
| 账户 | AccountNumber() | int | 账户号码 |
| 账户 | AccountCurrency() | string | 账户入金货币 |
| 市场 | Bid | double | 当前买价 |
| 市场 | Ask | double | 当前卖价 |
| 市场 | Point | double | 最小价格变动单位 |
| 市场 | Digits | int | 价格小数位数 |
| 市场 | MarketInfo() | double | 各种市场参数 |
| 市场 | TimeCurrent() | datetime | 当前服务器时间 |
| 市场 | RefreshRates() | bool | 更新Bid/Ask价格 |
三、账户信息函数 - 了解你的交易资金
```mql4
// 完整账户信息获取
void DisplayAccountInfo() {
Print("========== 账户信息 ==========");
Print("账户名称:", AccountName());
Print("账户号码:", AccountNumber());
Print("账户货币:", AccountCurrency());
Print("账户余额:", DoubleToString(AccountBalance(), 2));
Print("账户净值:", DoubleToString(AccountEquity(), 2));
Print("浮动盈亏:", DoubleToString(AccountProfit(), 2));
Print("可用保证金:", DoubleToString(AccountFreeMargin(), 2));
Print("已用保证金:", DoubleToString(AccountMargin(), 2));
Print("保证金水平:", DoubleToString(AccountFreeMarginMode() == 0 ?
(AccountEquity()/AccountMargin())*100 : 0, 2), "%");
Print("杠杆:1:", AccountLeverage());
Print("爆仓模式:", AccountStopoutMode() == 0 ? "百分比" : "金额");
Print("爆仓水平:", AccountStopoutLevel());
Print("==========================================");
}
// 检查账户是否有足够的可用保证金
bool HasSufficientMargin(double lotSize) {
double requiredMargin = MarketInfo(Symbol(), MODE_MARGINREQUIRED) * lotSize;
double freeMargin = AccountFreeMargin();
Print("所需保证金:", requiredMargin);
Print("可用保证金:", freeMargin);
return (freeMargin >= requiredMargin * 1.2); // 保留20%缓冲区
}
// 基于可用保证金计算最大允许手数
double GetMaxLotByMargin() {
double freeMargin = AccountFreeMargin();
double marginRequired = MarketInfo(Symbol(), MODE_MARGINREQUIRED);
if(marginRequired <= 0) return 0.01;
double maxLots = freeMargin / marginRequired;
double stepSize = MarketInfo(Symbol(), MODE_LOTSTEP);
double maxAllowed = MarketInfo(Symbol(), MODE_MAXLOT);
maxLots = MathFloor(maxLots / stepSize) * stepSize;
maxLots = MathMin(maxLots, maxAllowed);
return NormalizeDouble(maxLots, 2);
}
// 基于风险的仓位大小计算(净值百分比)
double CalculateLotByRiskPercent(double riskPercent, double stopLossPoints) {
double equity = AccountEquity();
double riskAmount = equity * riskPercent / 100;
double pointValue = MarketInfo(Symbol(), MODE_TICKVALUE);
if(pointValue <= 0 || stopLossPoints <= 0) return 0.01;
double rawLots = riskAmount / (stopLossPoints * pointValue);
double stepSize = MarketInfo(Symbol(), MODE_LOTSTEP);
double minLot = MarketInfo(Symbol(), MODE_MINLOT);
double maxLot = MarketInfo(Symbol(), MODE_MAXLOT);
rawLots = MathFloor(rawLots / stepSize) * stepSize;
rawLots = MathMax(minLot, MathMin(maxLot, rawLots));
return NormalizeDouble(rawLots, 2);
}
// 跟踪每日亏损限额
bool IsDailyLossExceeded(double maxDailyLossPercent) {
static double startEquity = 0;
static datetime lastReset = 0;
datetime currentDay = GetMidnight();
if(currentDay != lastReset) {
startEquity = AccountEquity();
lastReset = currentDay;
}
double currentEquity = AccountEquity();
double lossPercent = (startEquity - currentEquity) / startEquity * 100;
if(lossPercent >= maxDailyLossPercent) {
Print("每日亏损限额已达:", lossPercent, "%");
return true;
}
return false;
}
// 获取当日午夜零点时间戳
datetime GetMidnight() {
MqlDateTime tm;
TimeToStruct(TimeCurrent(), tm);
tm.hour = 0;
tm.min = 0;
tm.sec = 0;
return StructToTime(tm);
}
```
四、市场信息函数(MarketInfo)- 了解交易条件
```mql4
// 完整MarketInfo参数参考
void DisplayMarketInfo() {
Print("========== 市场信息 ==========");
Print("品种:", Symbol());
Print("买价:", DoubleToString(Bid, Digits));
Print("卖价:", DoubleToString(Ask, Digits));
Print("点差:", MarketInfo(Symbol(), MODE_SPREAD));
Print("点值:", DoubleToString(Point, 5));
Print("小数位数:", Digits);
Print("最小变动价位:", MarketInfo(Symbol(), MODE_TICKSIZE));
Print("每点价值:", MarketInfo(Symbol(), MODE_TICKVALUE));
Print("最小手数:", MarketInfo(Symbol(), MODE_MINLOT));
Print("最大手数:", MarketInfo(Symbol(), MODE_MAXLOT));
Print("手数步长:", MarketInfo(Symbol(), MODE_LOTSTEP));
Print("最小止损距离:", MarketInfo(Symbol(), MODE_STOPLEVEL));
Print("订单冻结距离:", MarketInfo(Symbol(), MODE_FREEZELEVEL));
Print("每手保证金:", MarketInfo(Symbol(), MODE_MARGINREQUIRED));
Print("========================================");
}
// MarketInfo参数常量参考
/*
MODE_LOW - 当日最低价
MODE_HIGH - 当日最高价
MODE_TIME - 最后服务器时间
MODE_BID - 当前买价
MODE_ASK - 当前卖价
MODE_POINT - 点值
MODE_DIGITS - 价格小数位数
MODE_SPREAD - 当前点差
MODE_STOPLEVEL - 最小止损距离(点数)
MODE_FREEZELEVEL - 订单冻结距离(点数)
MODE_LOTSIZE - 基础货币的合约大小
MODE_TICKVALUE - 点值(入金货币)
MODE_TICKSIZE - 最小变动价位
MODE_SWAPLONG - 多头隔夜利息
MODE_SWAPSHORT - 空头隔夜利息
MODE_MINLOT - 最小手数
MODE_MAXLOT - 最大手数
MODE_LOTSTEP - 手数步长
MODE_MARGINREQUIRED - 每手所需保证金
*/
// 检查点差是否可接受
bool IsSpreadAcceptable(int maxSpread) {
int currentSpread = (int)MarketInfo(Symbol(), MODE_SPREAD);
if(currentSpread <= maxSpread) {
return true;
}
Print("点差过高:", currentSpread, "(最大允许:", maxSpread, ")");
return false;
}
// 验证止损距离
bool IsStopLossValid(double stopLossPrice, int orderType) {
double currentPrice = (orderType == OP_BUY) ? Ask : Bid;
int minDistance = (int)MarketInfo(Symbol(), MODE_STOPLEVEL);
double distance = (orderType == OP_BUY) ?
(currentPrice - stopLossPrice) / Point :
(stopLossPrice - currentPrice) / Point;
if(distance >= minDistance) {
return true;
}
Print("止损距离太近:", distance, "(最小要求:", minDistance, ")");
return false;
}
// 获取符合经纪商限制的有效手数
double GetValidLotSize(double requestedLot) {
double minLot = MarketInfo(Symbol(), MODE_MINLOT);
double maxLot = MarketInfo(Symbol(), MODE_MAXLOT);
double stepSize = MarketInfo(Symbol(), MODE_LOTSTEP);
// 限制在最小/最大范围内
double validLot = MathMax(minLot, MathMin(maxLot, requestedLot));
// 按步长舍入
validLot = MathFloor(validLot / stepSize) * stepSize;
validLot = NormalizeDouble(validLot, 2);
Print("请求手数:", requestedLot, " -> 有效手数:", validLot);
return validLot;
}
// 计算仓位所需的保证金
double CalculateRequiredMargin(double lotSize) {
double marginPerLot = MarketInfo(Symbol(), MODE_MARGINREQUIRED);
double totalMargin = marginPerLot * lotSize;
Print("开立", lotSize, "手所需的保证金:", totalMargin);
return totalMargin;
}
// 获取账户货币的点值
double GetTickValue() {
double tickValue = MarketInfo(Symbol(), MODE_TICKVALUE);
return tickValue;
}
// 计算账户货币中的点值
double CalculatePointValue(double lotSize) {
double tickValue = MarketInfo(Symbol(), MODE_TICKVALUE);
double pointValue = tickValue * lotSize;
return pointValue;
}
```
五、Bid/Ask/Point/Digits - 实时价格访问
```mql4
// 交易前刷新报价(关键步骤)
void RefreshTradingRates() {
RefreshRates(); // 更新Bid和Ask变量
Print("已刷新报价 - 买价:", DoubleToString(Bid, Digits),
" 卖价:", DoubleToString(Ask, Digits));
}
// 带报价刷新的专业订单执行
int ExecuteOrderWithRefresh(int orderType, double lotSize, double stopLossPoints, double takeProfitPoints) {
// 每次OrderSend前务必刷新报价
RefreshRates();
double price = (orderType == OP_BUY) ? Ask : Bid;
int digits = Digits;
int stopLevel = (int)MarketInfo(Symbol(), MODE_STOPLEVEL);
double sl = 0, tp = 0;
if(stopLossPoints > 0) {
if(orderType == OP_BUY) {
sl = NormalizeDouble(price - MathMax(stopLossPoints, stopLevel) * Point, digits);
} else {
sl = NormalizeDouble(price + MathMax(stopLossPoints, stopLevel) * Point, digits);
}
}
if(takeProfitPoints > 0) {
if(orderType == OP_BUY) {
tp = NormalizeDouble(price + takeProfitPoints * Point, digits);
} else {
tp = NormalizeDouble(price - takeProfitPoints * Point, digits);
}
}
int ticket = OrderSend(Symbol(), orderType, lotSize, price, 30, sl, tp, "价格EA", 12345, 0, clrNONE);
return ticket;
}
// 以点数计算盈亏
double CalculateProfitInPoints(double entryPrice, double exitPrice, int orderType) {
double points;
if(orderType == OP_BUY) {
points = (exitPrice - entryPrice) / Point;
} else {
points = (entryPrice - exitPrice) / Point;
}
return points;
}
// 以账户货币计算盈亏
double CalculateProfitInCurrency(double entryPrice, double exitPrice, double lotSize, int orderType) {
double points = CalculateProfitInPoints(entryPrice, exitPrice, orderType);
double pointValue = MarketInfo(Symbol(), MODE_TICKVALUE);
double profit = points * pointValue * lotSize;
return profit;
}
```
六、完整风险管理管理系统
```mql4
//+------------------------------------------------------------------+
//| 完整风险管理管理系统 |
//+------------------------------------------------------------------+
class RiskManager {
private:
int magicNumber;
double maxDailyLossPercent;
double maxDrawdownPercent;
int maxDailyTrades;
int maxConcurrentPositions;
double maxRiskPerTradePercent;
int todayTrades;
double startingEquity;
datetime lastReset;
void ResetDaily() {
datetime midnight = GetMidnight();
if(midnight != lastReset) {
startingEquity = AccountEquity();
todayTrades = 0;
lastReset = midnight;
Print("风险管理器:每日计数器已重置。起始净值:", startingEquity);
}
}
datetime GetMidnight() {
MqlDateTime tm;
TimeToStruct(TimeCurrent(), tm);
tm.hour = 0;
tm.min = 0;
tm.sec = 0;
return StructToTime(tm);
}
public:
RiskManager(int magic) {
magicNumber = magic;
maxDailyLossPercent = 5.0;
maxDrawdownPercent = 15.0;
maxDailyTrades = 10;
maxConcurrentPositions = 3;
maxRiskPerTradePercent = 2.0;
todayTrades = 0;
startingEquity = AccountEquity();
lastReset = GetMidnight();
}
void SetMaxDailyLossPercent(double percent) { maxDailyLossPercent = percent; }
void SetMaxDrawdownPercent(double percent) { maxDrawdownPercent = percent; }
void SetMaxDailyTrades(int trades) { maxDailyTrades = trades; }
void SetMaxConcurrentPositions(int positions) { maxConcurrentPositions = positions; }
void SetMaxRiskPerTradePercent(double percent) { maxRiskPerTradePercent = percent; }
bool CanTrade() {
ResetDaily();
// 检查每日亏损限额
double currentEquity = AccountEquity();
double lossPercent = (startingEquity - currentEquity) / startingEquity * 100;
if(lossPercent >= maxDailyLossPercent) {
Print("风险管理器:每日亏损限额已达。亏损:", lossPercent, "%");
return false;
}
// 检查每日交易次数
if(todayTrades >= maxDailyTrades) {
Print("风险管理器:每日交易次数限额已达。交易次数:", todayTrades);
return false;
}
// 检查并发持仓数
int openPositions = CountOpenPositions();
if(openPositions >= maxConcurrentPositions) {
Print("风险管理器:最大并发持仓数已达。持仓数:", openPositions);
return false;
}
// 检查最大回撤
double peakEquity = GetPeakEquity();
double drawdownPercent = (peakEquity - currentEquity) / peakEquity * 100;
if(drawdownPercent >= maxDrawdownPercent) {
Print("风险管理器:最大回撤已达。回撤:", drawdownPercent, "%");
return false;
}
// 检查可用保证金
double freeMargin = AccountFreeMargin();
double minMarginRequired = freeMargin * 0.2; // 保留20%缓冲区
if(freeMargin < minMarginRequired) {
Print("风险管理器:可用保证金不足。可用:", freeMargin);
return false;
}
return true;
}
double CalculateLotSize(double stopLossPoints) {
double equity = AccountEquity();
double riskAmount = equity * maxRiskPerTradePercent / 100;
double pointValue = MarketInfo(Symbol(), MODE_TICKVALUE);
if(pointValue <= 0 || stopLossPoints <= 0) return 0.01;
double rawLots = riskAmount / (stopLossPoints * pointValue);
double stepSize = MarketInfo(Symbol(), MODE_LOTSTEP);
double minLot = MarketInfo(Symbol(), MODE_MINLOT);
double maxLot = MarketInfo(Symbol(), MODE_MAXLOT);
rawLots = MathFloor(rawLots / stepSize) * stepSize;
rawLots = MathMax(minLot, MathMin(maxLot, rawLots));
return NormalizeDouble(rawLots, 2);
}
void RecordTrade() {
todayTrades++;
Print("风险管理器:交易已记录。今日总数:", todayTrades);
}
int CountOpenPositions() {
int count = 0;
for(int i = 0; i < OrdersTotal(); i++) {
if(OrderSelect(i, SELECT_BY_POS, MODE_TRADES)) {
if(OrderMagicNumber() == magicNumber && OrderSymbol() == Symbol()) {
count++;
}
}
}
return count;
}
double GetPeakEquity() {
static double peakEquity = 0;
double currentEquity = AccountEquity();
if(currentEquity > peakEquity) {
peakEquity = currentEquity;
}
return peakEquity;
}
void PrintStatus() {
Print("========== 风险管理器状态 ==========");
Print("余额:", DoubleToString(AccountBalance(), 2));
Print("净值:", DoubleToString(AccountEquity(), 2));
Print("可用保证金:", DoubleToString(AccountFreeMargin(), 2));
Print("每日亏损:", DoubleToString((startingEquity - AccountEquity())/startingEquity*100, 2), "%");
Print("今日交易次数:", todayTrades, "/", maxDailyTrades);
Print("持仓数:", CountOpenPositions(), "/", maxConcurrentPositions);
Print("==========================================");
}
};
```
七、完整EA(带风险管理)
```mql4
//+------------------------------------------------------------------+
//| RiskManagedEA.mq4 |
//+------------------------------------------------------------------+
#property copyright "Copyright 2024"
#property version "1.00"
#property strict
input double InpRiskPercent = 2.0;
input int InpMagic = 12345;
input int InpMaxSpread = 30;
input int InpTrailingStop = 30;
RiskManager riskMgr(InpMagic);
//+------------------------------------------------------------------+
//| EA初始化函数 |
//+------------------------------------------------------------------+
int OnInit() {
riskMgr.SetMaxDailyLossPercent(5.0);
riskMgr.SetMaxDailyTrades(10);
riskMgr.SetMaxConcurrentPositions(3);
riskMgr.SetMaxRiskPerTradePercent(InpRiskPercent);
Print("带风险管理的EA已初始化");
return(INIT_SUCCEEDED);
}
//+------------------------------------------------------------------+
//| EA主执行函数 |
//+------------------------------------------------------------------+
void OnTick() {
static datetime lastBarTime = 0;
if(Time[0] == lastBarTime) return;
lastBarTime = Time[0];
// 对现有持仓应用移动止损
ManageExistingPositions();
// 检查是否可以交易
if(!riskMgr.CanTrade()) {
riskMgr.PrintStatus();
return;
}
// 检查点差
if(!IsSpreadAcceptable(InpMaxSpread)) {
return;
}
// 生成信号
int signal = GenerateSignal();
if(signal == SIGNAL_BUY) {
ExecuteTrade(OP_BUY);
} else if(signal == SIGNAL_SELL) {
ExecuteTrade(OP_SELL);
}
}
// 其他辅助函数(GenerateSignal、ExecuteTrade、ManageExistingPositions等)
```
八、账户与市场函数最佳实践清单
参考来源:
9. 下一步
第15篇将讲解MQL4常见编译错误及解决方案 – 错误1、17、30、31、33、130、4051等的完整修复指南,附带实际调试技巧。