//±-----------------------------------------------------------------+
//| 马丁加仓EA.mq5 - 完整修复版 |
//| Copyright 2023, MetaQuotes Software Corp. |
//| https://www.mql5.com |
//±-----------------------------------------------------------------+
#property copyright "Copyright 2023"
#property version "1.03" // 修复版
#property script_show_inputs
#property strict
input double BaseVolume = 0.01; // 基础手数
input int GapPoints = 30; // 加仓点数间隔
input double MarginSafetyRatio = 1.5; // 保证金安全系数
input double MinFreeMargin = 100; // 最低保留保证金(USD)
#include <Trade/Trade.mqh>
#include <Trade/SymbolInfo.mqh>
#include <Trade/PositionInfo.mqh> // 添加头文件
CTrade trade;
CSymbolInfo symbolInfo;
//— 全局变量
enum ENUM_MODE {MODE_NONE, MODE_BUY, MODE_SELL};
ENUM_MODE currentMode = MODE_NONE; // 当前模式:无/做多/做空
double lastBuyPrice = 0.0; // 上一次做多开仓价
double lastSellPrice = 0.0; // 上一次做空开仓价
int orderCount = 0; // 订单计数器
//±-----------------------------------------------------------------+
//| EA初始化函数 |
//±-----------------------------------------------------------------+
int OnInit()
{
// 初始化品种信息
if(!symbolInfo.Name(_Symbol))
{
Print("初始化品种信息失败!");
return(INIT_FAILED);
}
// 创建交易按钮
CreateButton("一键做多", 10, 50, 100, 30, clrGreen, "StartBuy");
CreateButton("一键做空", 120, 50, 100, 30, clrRed, "StartSell");
CreateButton("一键清仓", 230, 50, 100, 30, clrGray, "CloseAll");
CreateButton("一键锁仓", 340, 50, 100, 30, clrBlue, "LockPos");
CreateButton("平盈利单", 10, 90, 100, 30, clrGold, "CloseProfit");
CreateButton("平亏损单", 120, 90, 100, 30, clrOrange, "CloseLoss");
CreateButton("平多单盈利", 230, 90, 100, 30, clrGreen, "CloseBuyProfit");
CreateButton("平空单盈利", 340, 90, 100, 30, clrRed, "CloseSellProfit");
// 创建手数和间距输入框
CreateLabel("开仓手数:", 10, 130, 80, 20, clrWhite, "LabelVolume");
CreateEditBox("0.01", 90, 130, 80, 20, clrWhite, "InputVolume");
CreateLabel("加仓间距(点):", 180, 130, 100, 20, clrWhite, "LabelGap");
CreateEditBox("30", 280, 130, 80, 20, clrWhite, "InputGap");
// 添加保证金设置
CreateLabel("安全系数:", 10, 160, 80, 20, clrWhite, "LabelMarginRatio");
CreateEditBox("1.5", 90, 160, 80, 20, clrWhite, "InputMarginRatio");
CreateLabel("保留保证金:", 180, 160, 100, 20, clrWhite, "LabelMinMargin");
CreateEditBox("100", 280, 160, 80, 20, clrWhite, "InputMinMargin");
// 添加最大层数设置
CreateLabel("最大层数:", 10, 190, 80, 20, clrWhite, "LabelMaxLayers");
CreateEditBox("10", 90, 190, 80, 20, clrWhite, "InputMaxLayers");
// 设置初始值
ObjectSetString(0, "InputVolume", OBJPROP_TEXT, DoubleToString(BaseVolume, 2));
ObjectSetString(0, "InputGap", OBJPROP_TEXT, IntegerToString(GapPoints));
ObjectSetString(0, "InputMarginRatio", OBJPROP_TEXT, DoubleToString(MarginSafetyRatio, 2));
ObjectSetString(0, "InputMinMargin", OBJPROP_TEXT, DoubleToString(MinFreeMargin, 2));
// 设置异步交易模式
trade.SetAsyncMode(true);
// 输出关键信息
Print("EA初始化完成 账户杠杆: 1:", AccountInfoInteger(ACCOUNT_LEVERAGE));
Print("当前品种: ", _Symbol, " 合约大小: ", symbolInfo.ContractSize());
return(INIT_SUCCEEDED);
}
//±-----------------------------------------------------------------+
//| 保证金检查函数 - 修复版 |
//±-----------------------------------------------------------------+
bool CheckMarginSufficient(ENUM_ORDER_TYPE orderType, double volume)
{
// 计算所需保证金
double marginRequired = 0.0;
double price = 0.0;
if(orderType == ORDER_TYPE_BUY)
price = SymbolInfoDouble(_Symbol, SYMBOL_ASK);
else
price = SymbolInfoDouble(_Symbol, SYMBOL_BID);
// 黄金的特殊保证金计算
if(_Symbol == "XAUUSD" || _Symbol == "GOLD")
{
// 黄金保证金 = (手数 * 合约大小 * 当前价格) / 账户杠杆
marginRequired = (volume * symbolInfo.ContractSize() * price) /
AccountInfoInteger(ACCOUNT_LEVERAGE);
}
else
{
// 标准保证金计算
marginRequired = (volume * symbolInfo.ContractSize() * price) /
AccountInfoInteger(ACCOUNT_LEVERAGE);
}
// 获取当前可用保证金
double freeMargin = AccountInfoDouble(ACCOUNT_MARGIN_FREE);
double minMargin = GetMinFreeMargin();
double safetyRatio = GetMarginSafetyRatio();
// 添加安全系数检查
if(freeMargin > (marginRequired * safetyRatio) &&
(freeMargin - marginRequired) > minMargin)
{
Print("保证金充足: 需要=", marginRequired, " 可用=", freeMargin,
" 安全系数=", safetyRatio, " 保留=", minMargin);
return true;
}
Print("保证金不足! 需要: ", marginRequired, " 可用: ", freeMargin,
" 安全系数: ", safetyRatio, " 需保留: ", minMargin);
return false;
}
//±-----------------------------------------------------------------+
//| 获取保证金安全系数 |
//±-----------------------------------------------------------------+
double GetMarginSafetyRatio()
{
string text = ObjectGetString(0, "InputMarginRatio", OBJPROP_TEXT);
double ratio = StringToDouble(text);
return(ratio > 1.0 ? ratio : MarginSafetyRatio);
}
//±-----------------------------------------------------------------+
//| 获取最低保留保证金 |
//±-----------------------------------------------------------------+
double GetMinFreeMargin()
{
string text = ObjectGetString(0, "InputMinMargin", OBJPROP_TEXT);
double margin = StringToDouble(text);
return(margin > 0 ? margin : MinFreeMargin);
}
//±-----------------------------------------------------------------+
//| 获取当前基础手数 |
//±-----------------------------------------------------------------+
double GetBaseVolume()
{
string text = ObjectGetString(0, "InputVolume", OBJPROP_TEXT);
double volume = StringToDouble(text);
return(volume > 0 ? volume : BaseVolume);
}
//±-----------------------------------------------------------------+
//| 获取当前加仓间距 |
//±-----------------------------------------------------------------+
int GetGapPoints()
{
string text = ObjectGetString(0, "InputGap", OBJPROP_TEXT);
int gap = (int)StringToInteger(text);
return(gap > 0 ? gap : GapPoints);
}
//±-----------------------------------------------------------------+
//| 获取当前仓位数量 |
//±-----------------------------------------------------------------+
int GetPositionCount()
{
int count = 0;
for(int i = 0; i < PositionsTotal(); i++)
{
ulong ticket = PositionGetTicket(i);
if(ticket > 0 && PositionGetString(POSITION_SYMBOL) == _Symbol)
{
count++;
}
}
return count;
}
//±-----------------------------------------------------------------+
//| EA订单逻辑处理函数 - 添加保证金检查 |
//±-----------------------------------------------------------------+
void OnTick()
{
// 马丁加仓逻辑
if(currentMode == MODE_BUY)
{
double currentBid = SymbolInfoDouble(_Symbol, SYMBOL_BID);
double pointValue = SymbolInfoDouble(_Symbol, SYMBOL_POINT);
// 获取当前加仓间距
int currentGapPoints = GetGapPoints();
// 检查是否需要加仓(价格下跌指定点数)
if(lastBuyPrice - currentBid >= currentGapPoints * pointValue)
{
double volume = GetBaseVolume();
// 添加保证金检查
if(CheckMarginSufficient(ORDER_TYPE_BUY, volume))
{
double ask = SymbolInfoDouble(_Symbol, SYMBOL_ASK);
if(trade.Buy(volume, _Symbol, ask, 0, 0))
{
lastBuyPrice = ask;
orderCount++;
Print("加仓做多: ", volume, "手 @ ", ask, " 当前订单数: ", orderCount);
}
else
{
Print("加仓做多失败! 错误代码: ", GetLastError());
}
}
else
{
Print("保证金不足,无法加仓做多");
}
}
}
else if(currentMode == MODE_SELL)
{
double currentAsk = SymbolInfoDouble(_Symbol, SYMBOL_ASK);
double pointValue = SymbolInfoDouble(_Symbol, SYMBOL_POINT);
// 获取当前加仓间距
int currentGapPoints = GetGapPoints();
// 检查是否需要加仓(价格上涨指定点数)
if(currentAsk - lastSellPrice >= currentGapPoints * pointValue)
{
double volume = GetBaseVolume();
// 添加保证金检查
if(CheckMarginSufficient(ORDER_TYPE_SELL, volume))
{
double bid = SymbolInfoDouble(_Symbol, SYMBOL_BID);
if(trade.Sell(volume, _Symbol, bid, 0, 0))
{
lastSellPrice = bid;
orderCount++;
Print("加仓做空: ", volume, "手 @ ", bid, " 当前订单数: ", orderCount);
}
else
{
Print("加仓做空失败! 错误代码: ", GetLastError());
}
}
else
{
Print("保证金不足,无法加仓做空");
}
}
}
}
//±-----------------------------------------------------------------+
//| 创建按钮函数 |
//±-----------------------------------------------------------------+
void CreateButton(string text, int x, int y, int width, int height, color bgColor, string name)
{
if(!ObjectCreate(0, name, OBJ_BUTTON, 0, 0, 0))
{
Print("创建按钮失败: ", name);
return;
}
ObjectSetInteger(0, name, OBJPROP_XDISTANCE, x);
ObjectSetInteger(0, name, OBJPROP_YDISTANCE, y);
ObjectSetInteger(0, name, OBJPROP_XSIZE, width);
ObjectSetInteger(0, name, OBJPROP_YSIZE, height);
ObjectSetString(0, name, OBJPROP_TEXT, text);
ObjectSetInteger(0, name, OBJPROP_BGCOLOR, bgColor);
ObjectSetInteger(0, name, OBJPROP_COLOR, clrWhite);
ObjectSetInteger(0, name, OBJPROP_FONTSIZE, 9);
}
//±-----------------------------------------------------------------+
//| 创建标签函数 |
//±-----------------------------------------------------------------+
void CreateLabel(string text, int x, int y, int width, int height, color textColor, string name)
{
if(!ObjectCreate(0, name, OBJ_LABEL, 0, 0, 0))
{
Print("创建标签失败: ", name);
return;
}
ObjectSetInteger(0, name, OBJPROP_XDISTANCE, x);
ObjectSetInteger(0, name, OBJPROP_YDISTANCE, y);
ObjectSetInteger(0, name, OBJPROP_XSIZE, width);
ObjectSetInteger(0, name, OBJPROP_YSIZE, height);
ObjectSetString(0, name, OBJPROP_TEXT, text);
ObjectSetInteger(0, name, OBJPROP_COLOR, textColor);
ObjectSetInteger(0, name, OBJPROP_FONTSIZE, 9);
}
//±-----------------------------------------------------------------+
//| 创建编辑框函数 |
//±-----------------------------------------------------------------+
void CreateEditBox(string text, int x, int y, int width, int height, color textColor, string name)
{
if(!ObjectCreate(0, name, OBJ_EDIT, 0, 0, 0))
{
Print("创建编辑框失败: ", name);
return;
}
ObjectSetInteger(0, name, OBJPROP_XDISTANCE, x);
ObjectSetInteger(0, name, OBJPROP_YDISTANCE, y);
ObjectSetInteger(0, name, OBJPROP_XSIZE, width);
ObjectSetInteger(0, name, OBJPROP_YSIZE, height);
ObjectSetString(0, name, OBJPROP_TEXT, text);
ObjectSetInteger(0, name, OBJPROP_COLOR, textColor);
ObjectSetInteger(0, name, OBJPROP_BGCOLOR, clrBlack);
ObjectSetInteger(0, name, OBJPROP_BORDER_COLOR, clrSilver);
ObjectSetInteger(0, name, OBJPROP_FONTSIZE, 9);
ObjectSetInteger(0, name, OBJPROP_ALIGN, ALIGN_CENTER);
}
//±-----------------------------------------------------------------+
//| 图表事件处理函数 - 添加保证金检查 |
//±-----------------------------------------------------------------+
void OnChartEvent(const int id, const long &lparam, const double &dparam, const string &sparam)
{
// 处理按钮点击事件
if(id == CHARTEVENT_OBJECT_CLICK)
{
//— 一键做多
if(sparam == "StartBuy")
{
double volume = GetBaseVolume();
if(CheckMarginSufficient(ORDER_TYPE_BUY, volume))
{
currentMode = MODE_BUY;
orderCount = 0; // 重置订单计数器
double ask = SymbolInfoDouble(_Symbol, SYMBOL_ASK);
if(trade.Buy(volume, _Symbol, ask, 0, 0))
{
lastBuyPrice = ask;
orderCount++;
Print("开始做多: ", volume, "手 @ ", ask);
}
else
{
Print("开始做多失败! 错误代码: ", GetLastError());
}
}
else
{
Print("保证金不足,无法开始做多");
}
}
//— 一键做空
else if(sparam == "StartSell")
{
double volume = GetBaseVolume();
if(CheckMarginSufficient(ORDER_TYPE_SELL, volume))
{
currentMode = MODE_SELL;
orderCount = 0; // 重置订单计数器
double bid = SymbolInfoDouble(_Symbol, SYMBOL_BID);
if(trade.Sell(volume, _Symbol, bid, 0, 0))
{
lastSellPrice = bid;
orderCount++;
Print("开始做空: ", volume, "手 @ ", bid);
}
else
{
Print("开始做空失败! 错误代码: ", GetLastError());
}
}
else
{
Print("保证金不足,无法开始做空");
}
}
//— 一键清仓
else if(sparam == "CloseAll")
{
currentMode = MODE_NONE;
CloseAllPositionsFast();
orderCount = 0; // 重置订单计数器
}
//— 一键锁仓
else if(sparam == "LockPos")
{
LockPositions();
}
//— 平止盈单
else if(sparam == "CloseProfit")
{
CloseProfitPositions();
}
//— 平亏损单
else if(sparam == "CloseLoss")
{
CloseLossPositions();
}
//— 平多单盈利单
else if(sparam == "CloseBuyProfit")
{
ClosePositionsByTypeAndProfit(POSITION_TYPE_BUY, true);
}
//— 平空单盈利单
else if(sparam == "CloseSellProfit")
{
ClosePositionsByTypeAndProfit(POSITION_TYPE_SELL, true);
}
}
}
//±-----------------------------------------------------------------+
//| 快速关闭所有持仓(批量平仓优化) |
//±-----------------------------------------------------------------+
void CloseAllPositionsFast()
{
int total = PositionsTotal();
if(total <= 0) return;
// 收集所有仓位的ticket
for(int i = total-1; i >= 0; i--)
{
ulong ticket = PositionGetTicket(i);
if(ticket > 0 && PositionGetString(POSITION_SYMBOL) == _Symbol)
{
trade.PositionClose(ticket);
}
}
Print("已关闭所有持仓");
}
//±-----------------------------------------------------------------+
//| 锁仓函数(批量优化) |
//±-----------------------------------------------------------------+
void LockPositions()
{
double buyVolume = 0.0;
double sellVolume = 0.0;
// 计算多空总手数
for(int i = 0; i < PositionsTotal(); i++)
{
ulong ticket = PositionGetTicket(i);
if(ticket > 0 && PositionGetString(POSITION_SYMBOL) == _Symbol)
{
if(PositionGetInteger(POSITION_TYPE) == POSITION_TYPE_BUY)
buyVolume += PositionGetDouble(POSITION_VOLUME);
else if(PositionGetInteger(POSITION_TYPE) == POSITION_TYPE_SELL)
sellVolume += PositionGetDouble(POSITION_VOLUME);
}
}
// 批量开对冲单
if(buyVolume > sellVolume)
{
double volume = buyVolume - sellVolume;
if(trade.Sell(volume, _Symbol, SymbolInfoDouble(_Symbol, SYMBOL_BID)))
Print("已锁仓: 卖出 ", volume, "手");
}
else if(sellVolume > buyVolume)
{
double volume = sellVolume - buyVolume;
if(trade.Buy(volume, _Symbol, SymbolInfoDouble(_Symbol, SYMBOL_ASK)))
Print("已锁仓: 买入 ", volume, "手");
}
else
{
Print("多空仓位已平衡,无需锁仓");
}
}
//±-----------------------------------------------------------------+
//| 平盈利单(批量优化) |
//±-----------------------------------------------------------------+
void CloseProfitPositions()
{
int count = 0;
for(int i = PositionsTotal()-1; i >= 0; i--)
{
ulong ticket = PositionGetTicket(i);
if(ticket > 0 && PositionGetString(POSITION_SYMBOL) == _Symbol)
{
double profit = PositionGetDouble(POSITION_PROFIT);
if(profit > 0)
{
trade.PositionClose(ticket);
count++;
}
}
}
Print("已平仓 ", count, " 个盈利单");
}
//±-----------------------------------------------------------------+
//| 平亏损单(批量优化) |
//±-----------------------------------------------------------------+
void CloseLossPositions()
{
int count = 0;
for(int i = PositionsTotal()-1; i >= 0; i--)
{
ulong ticket = PositionGetTicket(i);
if(ticket > 0 && PositionGetString(POSITION_SYMBOL) == _Symbol)
{
double profit = PositionGetDouble(POSITION_PROFIT);
if(profit < 0)
{
trade.PositionClose(ticket);
count++;
}
}
}
Print("已平仓 ", count, " 个亏损单");
}
//±-----------------------------------------------------------------+
//| 按类型和平仓条件关闭订单(批量优化) |
//±-----------------------------------------------------------------+
void ClosePositionsByTypeAndProfit(ENUM_POSITION_TYPE type, bool closeProfit)
{
int count = 0;
for(int i = PositionsTotal()-1; i >= 0; i--)
{
ulong ticket = PositionGetTicket(i);
if(ticket > 0 &&
PositionGetString(POSITION_SYMBOL) == _Symbol &&
PositionGetInteger(POSITION_TYPE) == type)
{
double profit = PositionGetDouble(POSITION_PROFIT);
if((closeProfit && profit > 0) || (!closeProfit && profit < 0))
{
trade.PositionClose(ticket);
count++;
}
}
}
Print("已平仓 ", count, " 个指定类型订单");
}
//±-----------------------------------------------------------------+
//| EA终止函数 |
//±-----------------------------------------------------------------+
void OnDeinit(const int reason)
{
// 删除所有图形对象
ObjectsDeleteAll(0, -1, OBJ_BUTTON);
ObjectsDeleteAll(0, -1, OBJ_LABEL);
ObjectsDeleteAll(0, -1, OBJ_EDIT);
Print("EA已卸载");
}
以上代码中,加仓的时候以前面开单的价格为标准,不以实时价格为标准,如果价格低于或者高于前面的价格达到30点的加仓间距再加仓,不要重复加仓