交易控制面板EA
可拖动面板,一键平仓、开仓、顺势逆势加仓、批量止盈止损、移动止盈
MT5 真机运行截图 · 服务器把编译好的 EX5 挂到黄金 H1 图表上截的,你装上去看到的就是这个样子
#property copyright "XAUOF Indicator Studio"
#property version "1.30"
#include <Trade\Trade.mqh>
//--- 输入参数
input group "=== 开仓设置 ==="
input double InpLotSize = 0.01; // 默认开仓手数
input double InpLotStep = 0.01; // 手数调节步长
input double InpMinLot = 0.01; // 最小手数
input double InpMaxLot = 10.0; // 最大手数
input group "=== 加仓设置 ==="
input bool InpEnableAddPosition = true; // 启用加仓功能
input double InpAddLotMultiplier = 1.5; // 加仓手数倍数(相对上一单)
input int InpTrendAddDistance = 200; // 顺势加仓间距(点数,盈利后)
input int InpCounterAddDistance = 300; // 逆势加仓间距(点数,浮亏后)
input int InpMaxAddTimes = 3; // 最大加仓次数
input group "=== 止盈止损设置 ==="
input int InpTakeProfit = 500; // 固定止盈点数(0=不设置)
input int InpStopLoss = 300; // 固定止损点数(0=不设置)
input bool InpAutoSetTPSL = true; // 开仓时自动设置止盈止损
input group "=== 移动止盈 ==="
input bool InpEnableTrailing = true; // 启用移动止盈
input int InpTrailStart = 300; // 移动止盈触发点数
input int InpTrailDistance = 150; // 移动止盈距离点数
input group "=== 保本设置 ==="
input int InpBreakEvenPoints = 50; // 保本后加点数(开仓价之上/下)
input group "=== 面板设置 ==="
input int InpPanelX = 20; // 面板初始X坐标
input int InpPanelY = 30; // 面板初始Y坐标
input color InpCloseColor = clrCrimson; // 平仓按钮颜色
input color InpOpenColor = clrForestGreen; // 开仓按钮颜色
input color InpAddColor = clrOrange; // 加仓按钮颜色
input color InpSetColor = clrDodgerBlue; // 设置按钮颜色
//--- 全局变量
CTrade trade;
int timerCounter = 0;
double currentLotSize = 0.01;
//--- 面板拖动相关
bool isDragging = false;
int dragStartX = 0;
int dragStartY = 0;
int panelX = 0;
int panelY = 0;
//--- 面板尺寸
int panelWidth = 140;
int panelHeight = 450;
//--- 对象名称常量
#define PANEL_BG "panelBG"
#define PANEL_TITLE "panelTitle"
#define BTN_OPEN_BUY "btnOpenBuy"
#define BTN_OPEN_SELL "btnOpenSell"
#define BTN_LOT_UP "btnLotUp"
#define BTN_LOT_DOWN "btnLotDown"
#define LABEL_LOT "lblLot"
#define BTN_ADD_TREND "btnAddTrend"
#define BTN_ADD_COUNTER "btnAddCounter"
#define BTN_CLOSE_ALL "btnCloseAll"
#define BTN_CLOSE_BUY "btnCloseBuy"
#define BTN_CLOSE_SELL "btnCloseSell"
#define BTN_SET_TP "btnSetTP"
#define BTN_SET_SL "btnSetSL"
#define BTN_BREAKEVEN "btnBreakEven"
#define LABEL_INFO "lblInfo"
//+------------------------------------------------------------------+
//| Expert initialization function |
//+------------------------------------------------------------------+
int OnInit()
{
trade.SetExpertMagicNumber(0);
trade.SetDeviationInPoints(10);
if(!trade.SetTypeFillingBySymbol(_Symbol))
trade.SetTypeFilling(ORDER_FILLING_FOK);
currentLotSize = InpLotSize;
currentLotSize = NormalizeDouble(MathMax(InpMinLot, MathMin(InpMaxLot, currentLotSize)), 2);
panelX = InpPanelX;
panelY = InpPanelY;
CreatePanel();
EventSetTimer(1);
return(INIT_SUCCEEDED);
}
//+------------------------------------------------------------------+
//| Expert deinitialization function |
//+------------------------------------------------------------------+
void OnDeinit(const int reason)
{
EventKillTimer();
DeletePanel();
}
//+------------------------------------------------------------------+
//| Expert tick function |
//+------------------------------------------------------------------+
void OnTick()
{
UpdateInfoLabel();
// 自动加仓检查
if(InpEnableAddPosition)
{
CheckAutoAddPosition();
}
}
//+------------------------------------------------------------------+
//| Timer function |
//+------------------------------------------------------------------+
void OnTimer()
{
timerCounter++;
if(timerCounter >= 1)
{
timerCounter = 0;
if(InpEnableTrailing)
TrailingStop();
}
UpdateInfoLabel();
}
//+------------------------------------------------------------------+
//| ChartEvent function |
//+------------------------------------------------------------------+
void OnChartEvent(const int id,
const long &lparam,
const double &dparam,
const string &sparam)
{
// 处理鼠标拖动
if(id == CHARTEVENT_MOUSE_MOVE)
{
int x = (int)lparam;
int y = (int)dparam;
int flags = (int)sparam;
if((flags & 1) == 1)
{
if(!isDragging)
{
if(x >= panelX && x <= panelX + panelWidth &&
y >= panelY && y <= panelY + 25)
{
isDragging = true;
dragStartX = x - panelX;
dragStartY = y - panelY;
}
}
else
{
panelX = x - dragStartX;
panelY = y - dragStartY;
if(panelX < 0) panelX = 0;
if(panelY < 0) panelY = 0;
int chartWidth = (int)ChartGetInteger(0, CHART_WIDTH_IN_PIXELS);
int chartHeight = (int)ChartGetInteger(0, CHART_HEIGHT_IN_PIXELS);
if(panelX + panelWidth > chartWidth)
panelX = chartWidth - panelWidth;
if(panelY + panelHeight > chartHeight)
panelY = chartHeight - panelHeight;
UpdatePanelPosition();
}
}
else
{
isDragging = false;
}
}
// 处理按钮点击
if(id == CHARTEVENT_OBJECT_CLICK)
{
if(sparam == BTN_OPEN_BUY)
{
OpenPosition(ORDER_TYPE_BUY);
ObjectSetInteger(0, BTN_OPEN_BUY, OBJPROP_STATE, false);
}
else if(sparam == BTN_OPEN_SELL)
{
OpenPosition(ORDER_TYPE_SELL);
ObjectSetInteger(0, BTN_OPEN_SELL, OBJPROP_STATE, false);
}
else if(sparam == BTN_ADD_TREND)
{
AddPositionTrend();
ObjectSetInteger(0, BTN_ADD_TREND, OBJPROP_STATE, false);
}
else if(sparam == BTN_ADD_COUNTER)
{
AddPositionCounter();
ObjectSetInteger(0, BTN_ADD_COUNTER, OBJPROP_STATE, false);
}
else if(sparam == BTN_LOT_UP)
{
currentLotSize = NormalizeDouble(currentLotSize + InpLotStep, 2);
if(currentLotSize > InpMaxLot)
currentLotSize = InpMaxLot;
UpdateLotLabel();
ObjectSetInteger(0, BTN_LOT_UP, OBJPROP_STATE, false);
}
else if(sparam == BTN_LOT_DOWN)
{
currentLotSize = NormalizeDouble(currentLotSize - InpLotStep, 2);
if(currentLotSize < InpMinLot)
currentLotSize = InpMinLot;
UpdateLotLabel();
ObjectSetInteger(0, BTN_LOT_DOWN, OBJPROP_STATE, false);
}
else if(sparam == BTN_CLOSE_ALL)
{
CloseAllPositions();
ObjectSetInteger(0, BTN_CLOSE_ALL, OBJPROP_STATE, false);
}
else if(sparam == BTN_CLOSE_BUY)
{
ClosePositionsByType(POSITION_TYPE_BUY);
ObjectSetInteger(0, BTN_CLOSE_BUY, OBJPROP_STATE, false);
}
else if(sparam == BTN_CLOSE_SELL)
{
ClosePositionsByType(POSITION_TYPE_SELL);
ObjectSetInteger(0, BTN_CLOSE_SELL, OBJPROP_STATE, false);
}
else if(sparam == BTN_SET_TP)
{
SetFixedTakeProfit();
ObjectSetInteger(0, BTN_SET_TP, OBJPROP_STATE, false);
}
else if(sparam == BTN_SET_SL)
{
SetFixedStopLoss();
ObjectSetInteger(0, BTN_SET_SL, OBJPROP_STATE, false);
}
else if(sparam == BTN_BREAKEVEN)
{
SetBreakEven();
ObjectSetInteger(0, BTN_BREAKEVEN, OBJPROP_STATE, false);
}
ChartRedraw();
}
}
//+------------------------------------------------------------------+
//| 创建控制面板 |
//+------------------------------------------------------------------+
void CreatePanel()
{
int btnWidth = 120;
int btnHeight = 30;
int smallBtn = 35;
int spacing = 5;
int margin = 10;
CreateBackground(PANEL_BG, panelX, panelY, panelWidth, panelHeight);
CreateLabel(PANEL_TITLE, panelX + 5, panelY + 5);
ObjectSetString(0, PANEL_TITLE, OBJPROP_TEXT, "≡ 交易控制面板");
ObjectSetInteger(0, PANEL_TITLE, OBJPROP_FONTSIZE, 10);
ObjectSetInteger(0, PANEL_TITLE, OBJPROP_COLOR, clrWhiteSmoke);
int y = panelY + 30;
int x = panelX + margin;
// 开仓按钮
CreateButton(BTN_OPEN_BUY, "开多", x, y, btnWidth, btnHeight, InpOpenColor);
y += btnHeight + spacing;
CreateButton(BTN_OPEN_SELL, "开空", x, y, btnWidth, btnHeight, InpOpenColor);
y += btnHeight + spacing;
// 手数调节
CreateButton(BTN_LOT_DOWN, "-", x, y, smallBtn, btnHeight, clrGray);
CreateLabel(LABEL_LOT, x + smallBtn + 5, y + 8);
CreateButton(BTN_LOT_UP, "+", x + smallBtn + 55, y, smallBtn, btnHeight, clrGray);
UpdateLotLabel();
y += btnHeight + spacing * 2;
// 加仓按钮
CreateButton(BTN_ADD_TREND, "顺势加仓", x, y, btnWidth, btnHeight, InpAddColor);
y += btnHeight + spacing;
CreateButton(BTN_ADD_COUNTER, "逆势加仓", x, y, btnWidth, btnHeight, InpAddColor);
y += btnHeight + spacing * 2;
// 平仓按钮
CreateButton(BTN_CLOSE_ALL, "一键平仓", x, y, btnWidth, btnHeight, InpCloseColor);
y += btnHeight + spacing;
CreateButton(BTN_CLOSE_BUY, "一键平多", x, y, btnWidth, btnHeight, InpCloseColor);
y += btnHeight + spacing;
CreateButton(BTN_CLOSE_SELL, "一键平空", x, y, btnWidth, btnHeight, InpCloseColor);
y += btnHeight + spacing * 2;
// 设置按钮
CreateButton(BTN_SET_TP, "设置止盈", x, y, btnWidth, btnHeight, InpSetColor);
y += btnHeight + spacing;
CreateButton(BTN_SET_SL, "设置止损", x, y, btnWidth, btnHeight, InpSetColor);
y += btnHeight + spacing;
CreateButton(BTN_BREAKEVEN, "一键保本", x, y, btnWidth, btnHeight, InpSetColor);
y += btnHeight + spacing * 2;
CreateLabel(LABEL_INFO, x, y);
}
//+------------------------------------------------------------------+
//| 创建背景 |
//+------------------------------------------------------------------+
void CreateBackground(string name, int x, int y, int width, int height)
{
ObjectDelete(0, name);
ObjectCreate(0, name, OBJ_RECTANGLE_LABEL, 0, 0, 0);
ObjectSetInteger(0, name, OBJPROP_XDISTANCE, x);
ObjectSetInteger(0, name, OBJPROP_YDISTANCE, y);
ObjectSetInteger(0, name, OBJPROP_XSIZE, width);
ObjectSetInteger(0, name, OBJPROP_YSIZE, height);
ObjectSetInteger(0, name, OBJPROP_BGCOLOR, clrBlack);
ObjectSetInteger(0, name, OBJPROP_BORDER_TYPE, BORDER_FLAT);
ObjectSetInteger(0, name, OBJPROP_CORNER, CORNER_LEFT_UPPER);
ObjectSetInteger(0, name, OBJPROP_COLOR, clrDimGray);
ObjectSetInteger(0, name, OBJPROP_STYLE, STYLE_SOLID);
ObjectSetInteger(0, name, OBJPROP_WIDTH, 1);
ObjectSetInteger(0, name, OBJPROP_BACK, false);
ObjectSetInteger(0, name, OBJPROP_SELECTABLE, false);
}
//+------------------------------------------------------------------+
//| 创建按钮 |
//+------------------------------------------------------------------+
void CreateButton(string name, string text, int x, int y, int width, int height, color clr)
{
ObjectDelete(0, name);
ObjectCreate(0, name, OBJ_BUTTON, 0, 0, 0);
ObjectSetInteger(0, name, OBJPROP_XDISTANCE, x);
ObjectSetInteger(0, name, OBJPROP_YDISTANCE, y);
ObjectSetInteger(0, name, OBJPROP_XSIZE, width);
ObjectSetInteger(0, name, OBJPROP_YSIZE, height);
ObjectSetInteger(0, name, OBJPROP_CORNER, CORNER_LEFT_UPPER);
ObjectSetInteger(0, name, OBJPROP_BGCOLOR, clr);
ObjectSetInteger(0, name, OBJPROP_COLOR, clrWhite);
ObjectSetInteger(0, name, OBJPROP_FONTSIZE, 9);
ObjectSetString(0, name, OBJPROP_TEXT, text);
ObjectSetInteger(0, name, OBJPROP_SELECTABLE, false);
}
//+------------------------------------------------------------------+
//| 创建标签 |
//+------------------------------------------------------------------+
void CreateLabel(string name, int x, int y)
{
ObjectDelete(0, name);
ObjectCreate(0, name, OBJ_LABEL, 0, 0, 0);
ObjectSetInteger(0, name, OBJPROP_XDISTANCE, x);
ObjectSetInteger(0, name, OBJPROP_YDISTANCE, y);
ObjectSetInteger(0, name, OBJPROP_CORNER, CORNER_LEFT_UPPER);
ObjectSetInteger(0, name, OBJPROP_COLOR, clrWhite);
ObjectSetInteger(0, name, OBJPROP_FONTSIZE, 8);
ObjectSetString(0, name, OBJPROP_TEXT, "");
}
//+------------------------------------------------------------------+
//| 更新面板位置 |
//+------------------------------------------------------------------+
void UpdatePanelPosition()
{
int btnWidth = 120;
int btnHeight = 30;
int smallBtn = 35;
int spacing = 5;
int margin = 10;
ObjectSetInteger(0, PANEL_BG, OBJPROP_XDISTANCE, panelX);
ObjectSetInteger(0, PANEL_BG, OBJPROP_YDISTANCE, panelY);
ObjectSetInteger(0, PANEL_TITLE, OBJPROP_XDISTANCE, panelX + 5);
ObjectSetInteger(0, PANEL_TITLE, OBJPROP_YDISTANCE, panelY + 5);
int y = panelY + 30;
int x = panelX + margin;
ObjectSetInteger(0, BTN_OPEN_BUY, OBJPROP_XDISTANCE, x);
ObjectSetInteger(0, BTN_OPEN_BUY, OBJPROP_YDISTANCE, y);
y += btnHeight + spacing;
ObjectSetInteger(0, BTN_OPEN_SELL, OBJPROP_XDISTANCE, x);
ObjectSetInteger(0, BTN_OPEN_SELL, OBJPROP_YDISTANCE, y);
y += btnHeight + spacing;
ObjectSetInteger(0, BTN_LOT_DOWN, OBJPROP_XDISTANCE, x);
ObjectSetInteger(0, BTN_LOT_DOWN, OBJPROP_YDISTANCE, y);
ObjectSetInteger(0, LABEL_LOT, OBJPROP_XDISTANCE, x + smallBtn + 5);
ObjectSetInteger(0, LABEL_LOT, OBJPROP_YDISTANCE, y + 8);
ObjectSetInteger(0, BTN_LOT_UP, OBJPROP_XDISTANCE, x + smallBtn + 55);
ObjectSetInteger(0, BTN_LOT_UP, OBJPROP_YDISTANCE, y);
y += btnHeight + spacing * 2;
ObjectSetInteger(0, BTN_ADD_TREND, OBJPROP_XDISTANCE, x);
ObjectSetInteger(0, BTN_ADD_TREND, OBJPROP_YDISTANCE, y);
y += btnHeight + spacing;
ObjectSetInteger(0, BTN_ADD_COUNTER, OBJPROP_XDISTANCE, x);
ObjectSetInteger(0, BTN_ADD_COUNTER, OBJPROP_YDISTANCE, y);
y += btnHeight + spacing * 2;
ObjectSetInteger(0, BTN_CLOSE_ALL, OBJPROP_XDISTANCE, x);
ObjectSetInteger(0, BTN_CLOSE_ALL, OBJPROP_YDISTANCE, y);
y += btnHeight + spacing;
ObjectSetInteger(0, BTN_CLOSE_BUY, OBJPROP_XDISTANCE, x);
ObjectSetInteger(0, BTN_CLOSE_BUY, OBJPROP_YDISTANCE, y);
y += btnHeight + spacing;
ObjectSetInteger(0, BTN_CLOSE_SELL, OBJPROP_XDISTANCE, x);
ObjectSetInteger(0, BTN_CLOSE_SELL, OBJPROP_YDISTANCE, y);
y += btnHeight + spacing * 2;
ObjectSetInteger(0, BTN_SET_TP, OBJPROP_XDISTANCE, x);
ObjectSetInteger(0, BTN_SET_TP, OBJPROP_YDISTANCE, y);
y += btnHeight + spacing;
ObjectSetInteger(0, BTN_SET_SL, OBJPROP_XDISTANCE, x);
ObjectSetInteger(0, BTN_SET_SL, OBJPROP_YDISTANCE, y);
y += btnHeight + spacing;
ObjectSetInteger(0, BTN_BREAKEVEN, OBJPROP_XDISTANCE, x);
ObjectSetInteger(0, BTN_BREAKEVEN, OBJPROP_YDISTANCE, y);
y += btnHeight + spacing * 2;
ObjectSetInteger(0, LABEL_INFO, OBJPROP_XDISTANCE, x);
ObjectSetInteger(0, LABEL_INFO, OBJPROP_YDISTANCE, y);
ChartRedraw();
}
//+------------------------------------------------------------------+
//| 删除面板 |
//+------------------------------------------------------------------+
void DeletePanel()
{
ObjectDelete(0, PANEL_BG);
ObjectDelete(0, PANEL_TITLE);
ObjectDelete(0, BTN_OPEN_BUY);
ObjectDelete(0, BTN_OPEN_SELL);
ObjectDelete(0, BTN_ADD_TREND);
ObjectDelete(0, BTN_ADD_COUNTER);
ObjectDelete(0, BTN_LOT_UP);
ObjectDelete(0, BTN_LOT_DOWN);
ObjectDelete(0, LABEL_LOT);
ObjectDelete(0, BTN_CLOSE_ALL);
ObjectDelete(0, BTN_CLOSE_BUY);
ObjectDelete(0, BTN_CLOSE_SELL);
ObjectDelete(0, BTN_SET_TP);
ObjectDelete(0, BTN_SET_SL);
ObjectDelete(0, BTN_BREAKEVEN);
ObjectDelete(0, LABEL_INFO);
}
//+------------------------------------------------------------------+
//| 更新手数标签 |
//+------------------------------------------------------------------+
void UpdateLotLabel()
{
string lotText = StringFormat("%.2f手", currentLotSize);
ObjectSetString(0, LABEL_LOT, OBJPROP_TEXT, lotText);
}
//+------------------------------------------------------------------+
//| 更新信息标签 |
//+------------------------------------------------------------------+
void UpdateInfoLabel()
{
int totalPos = PositionsTotal();
int buyCount = 0, sellCount = 0;
double totalProfit = 0.0;
for(int i = 0; i < totalPos; i++)
{
ulong ticket = PositionGetTicket(i);
if(ticket > 0 && PositionSelectByTicket(ticket))
{
if(PositionGetString(POSITION_SYMBOL) == _Symbol)
{
ENUM_POSITION_TYPE type = (ENUM_POSITION_TYPE)PositionGetInteger(POSITION_TYPE);
if(type == POSITION_TYPE_BUY)
buyCount++;
else if(type == POSITION_TYPE_SELL)
sellCount++;
totalProfit += PositionGetDouble(POSITION_PROFIT) + PositionGetDouble(POSITION_SWAP);
}
}
}
string info = StringFormat("持仓: %d\n多:%d 空:%d\n盈亏: %.2f",
buyCount + sellCount, buyCount, sellCount, totalProfit);
if(InpEnableTrailing)
info += "\n移动止盈: 开";
ObjectSetString(0, LABEL_INFO, OBJPROP_TEXT, info);
}
//+------------------------------------------------------------------+
//| 开仓 |
//+------------------------------------------------------------------+
void OpenPosition(ENUM_ORDER_TYPE orderType)
{
double ask = SymbolInfoDouble(_Symbol, SYMBOL_ASK);
double bid = SymbolInfoDouble(_Symbol, SYMBOL_BID);
double price = (orderType == ORDER_TYPE_BUY) ? ask : bid;
double tp = 0.0, sl = 0.0;
if(InpAutoSetTPSL)
{
if(orderType == ORDER_TYPE_BUY)
{
if(InpTakeProfit > 0)
tp = NormalizeDouble(price + InpTakeProfit * _Point, _Digits);
if(InpStopLoss > 0)
sl = NormalizeDouble(price - InpStopLoss * _Point, _Digits);
}
else
{
if(InpTakeProfit > 0)
tp = NormalizeDouble(price - InpTakeProfit * _Point, _Digits);
if(InpStopLoss > 0)
sl = NormalizeDouble(price + InpStopLoss * _Point, _Digits);
}
}
bool result = false;
if(orderType == ORDER_TYPE_BUY)
result = trade.Buy(currentLotSize, _Symbol, 0, sl, tp, "面板开仓");
else
result = trade.Sell(currentLotSize, _Symbol, 0, sl, tp, "面板开仓");
if(result)
{
Print("开仓成功:", (orderType == ORDER_TYPE_BUY ? "多单" : "空单"),
",手数:", currentLotSize, ",价格:", price);
}
else
{
Print("开仓失败:", trade.ResultRetcodeDescription());
}
}
//+------------------------------------------------------------------+
//| 顺势加仓(盈利方向) |
//+------------------------------------------------------------------+
void AddPositionTrend()
{
if(!InpEnableAddPosition)
{
Print("加仓功能未启用");
return;
}
double ask = SymbolInfoDouble(_Symbol, SYMBOL_ASK);
double bid = SymbolInfoDouble(_Symbol, SYMBOL_BID);
int total = PositionsTotal();
int added = 0;
for(int i = 0; i < total; i++)
{
ulong ticket = PositionGetTicket(i);
if(ticket > 0 && PositionSelectByTicket(ticket))
{
if(PositionGetString(POSITION_SYMBOL) == _Symbol)
{
double openPrice = PositionGetDouble(POSITION_PRICE_OPEN);
double lots = PositionGetDouble(POSITION_VOLUME);
ENUM_POSITION_TYPE type = (ENUM_POSITION_TYPE)PositionGetInteger(POSITION_TYPE);
// 检查是否盈利达到顺势加仓条件
bool canAdd = false;
if(type == POSITION_TYPE_BUY && bid > openPrice + InpTrendAddDistance * _Point)
canAdd = true;
else if(type == POSITION_TYPE_SELL && ask < openPrice - InpTrendAddDistance * _Point)
canAdd = true;
if(canAdd)
{
// 计算加仓手数
double newLot = NormalizeDouble(lots * InpAddLotMultiplier, 2);
if(newLot > InpMaxLot)
newLot = InpMaxLot;
// 计算止盈止损
double tp = 0.0, sl = 0.0;
double price = (type == POSITION_TYPE_BUY) ? ask : bid;
if(InpAutoSetTPSL)
{
if(type == POSITION_TYPE_BUY)
{
if(InpTakeProfit > 0)
tp = NormalizeDouble(price + InpTakeProfit * _Point, _Digits);
if(InpStopLoss > 0)
sl = NormalizeDouble(price - InpStopLoss * _Point, _Digits);
}
else
{
if(InpTakeProfit > 0)
tp = NormalizeDouble(price - InpTakeProfit * _Point, _Digits);
if(InpStopLoss > 0)
sl = NormalizeDouble(price + InpStopLoss * _Point, _Digits);
}
}
// 执行加仓
bool result = false;
if(type == POSITION_TYPE_BUY)
result = trade.Buy(newLot, _Symbol, 0, sl, tp, "顺势加仓");
else
result = trade.Sell(newLot, _Symbol, 0, sl, tp, "顺势加仓");
if(result)
added++;
}
}
}
}
Print("顺势加仓完成,已加仓 ", added, " 次");
}
//+------------------------------------------------------------------+
//| 逆势加仓(浮亏方向) |
//+------------------------------------------------------------------+
void AddPositionCounter()
{
if(!InpEnableAddPosition)
{
Print("加仓功能未启用");
return;
}
double ask = SymbolInfoDouble(_Symbol, SYMBOL_ASK);
double bid = SymbolInfoDouble(_Symbol, SYMBOL_BID);
int total = PositionsTotal();
int added = 0;
for(int i = 0; i < total; i++)
{
ulong ticket = PositionGetTicket(i);
if(ticket > 0 && PositionSelectByTicket(ticket))
{
if(PositionGetString(POSITION_SYMBOL) == _Symbol)
{
double openPrice = PositionGetDouble(POSITION_PRICE_OPEN);
double lots = PositionGetDouble(POSITION_VOLUME);
ENUM_POSITION_TYPE type = (ENUM_POSITION_TYPE)PositionGetInteger(POSITION_TYPE);
// 检查是否浮亏达到逆势加仓条件
bool canAdd = false;
if(type == POSITION_TYPE_BUY && bid < openPrice - InpCounterAddDistance * _Point)
canAdd = true;
else if(type == POSITION_TYPE_SELL && ask > openPrice + InpCounterAddDistance * _Point)
canAdd = true;
if(canAdd)
{
// 计算加仓手数
double newLot = NormalizeDouble(lots * InpAddLotMultiplier, 2);
if(newLot > InpMaxLot)
newLot = InpMaxLot;
// 计算止盈止损
double tp = 0.0, sl = 0.0;
double price = (type == POSITION_TYPE_BUY) ? ask : bid;
if(InpAutoSetTPSL)
{
if(type == POSITION_TYPE_BUY)
{
if(InpTakeProfit > 0)
tp = NormalizeDouble(price + InpTakeProfit * _Point, _Digits);
if(InpStopLoss > 0)
sl = NormalizeDouble(price - InpStopLoss * _Point, _Digits);
}
else
{
if(InpTakeProfit > 0)
tp = NormalizeDouble(price - InpTakeProfit * _Point, _Digits);
if(InpStopLoss > 0)
sl = NormalizeDouble(price + InpStopLoss * _Point, _Digits);
}
}
// 执行加仓
bool result = false;
if(type == POSITION_TYPE_BUY)
result = trade.Buy(newLot, _Symbol, 0, sl, tp, "逆势加仓");
else
result = trade.Sell(newLot, _Symbol, 0, sl, tp, "逆势加仓");
if(result)
added++;
}
}
}
}
Print("逆势加仓完成,已加仓 ", added, " 次");
}
//+------------------------------------------------------------------+
//| 自动检查加仓条件(按次数限制) |
//+------------------------------------------------------------------+
void CheckAutoAddPosition()
{
// 此函数为预留接口,可根据需要实现自动加仓逻辑
// 当前版本采用手动点击加仓按钮的方式
}
//+------------------------------------------------------------------+
//| 平所有仓位 |
//+------------------------------------------------------------------+
void CloseAllPositions()
{
int closed = 0;
int total = PositionsTotal();
for(int i = total - 1; i >= 0; i--)
{
ulong ticket = PositionGetTicket(i);
if(ticket > 0 && PositionSelectByTicket(ticket))
{
if(PositionGetString(POSITION_SYMBOL) == _Symbol)
{
if(trade.PositionClose(ticket))
closed++;
}
}
}
Print("一键平仓完成,已平 ", closed, " 个仓位");
}
//+------------------------------------------------------------------+
//| 按类型平仓 |
//+------------------------------------------------------------------+
void ClosePositionsByType(ENUM_POSITION_TYPE type)
{
int closed = 0;
int total = PositionsTotal();
for(int i = total - 1; i >= 0; i--)
{
ulong ticket = PositionGetTicket(i);
if(ticket > 0 && PositionSelectByTicket(ticket))
{
if(PositionGetString(POSITION_SYMBOL) == _Symbol)
{
ENUM_POSITION_TYPE posType = (ENUM_POSITION_TYPE)PositionGetInteger(POSITION_TYPE);
if(posType == type)
{
if(trade.PositionClose(ticket))
closed++;
}
}
}
}
string typeName = (type == POSITION_TYPE_BUY) ? "多单" : "空单";
Print("一键平", typeName, "完成,已平 ", closed, " 个仓位");
}
//+------------------------------------------------------------------+
//| 设置固定止盈 |
//+------------------------------------------------------------------+
void SetFixedTakeProfit()
{
if(InpTakeProfit <= 0)
{
Print("固定止盈点数未设置或无效");
return;
}
int modified = 0;
int total = PositionsTotal();
for(int i = 0; i < total; i++)
{
ulong ticket = PositionGetTicket(i);
if(ticket > 0 && PositionSelectByTicket(ticket))
{
if(PositionGetString(POSITION_SYMBOL) == _Symbol)
{
double openPrice = PositionGetDouble(POSITION_PRICE_OPEN);
double currentSL = PositionGetDouble(POSITION_SL);
ENUM_POSITION_TYPE type = (ENUM_POSITION_TYPE)PositionGetInteger(POSITION_TYPE);
double newTP = 0.0;
if(type == POSITION_TYPE_BUY)
newTP = NormalizeDouble(openPrice + InpTakeProfit * _Point, _Digits);
else
newTP = NormalizeDouble(openPrice - InpTakeProfit * _Point, _Digits);
if(trade.PositionModify(ticket, currentSL, newTP))
modified++;
}
}
}
Print("固定止盈设置完成,已修改 ", modified, " 个仓位,止盈点数: ", InpTakeProfit);
}
//+------------------------------------------------------------------+
//| 设置固定止损 |
//+------------------------------------------------------------------+
void SetFixedStopLoss()
{
if(InpStopLoss <= 0)
{
Print("固定止损点数未设置或无效");
return;
}
int modified = 0;
int total = PositionsTotal();
for(int i = 0; i < total; i++)
{
ulong ticket = PositionGetTicket(i);
if(ticket > 0 && PositionSelectByTicket(ticket))
{
if(PositionGetString(POSITION_SYMBOL) == _Symbol)
{
double openPrice = PositionGetDouble(POSITION_PRICE_OPEN);
double currentTP = PositionGetDouble(POSITION_TP);
ENUM_POSITION_TYPE type = (ENUM_POSITION_TYPE)PositionGetInteger(POSITION_TYPE);
double newSL = 0.0;
if(type == POSITION_TYPE_BUY)
newSL = NormalizeDouble(openPrice - InpStopLoss * _Point, _Digits);
else
newSL = NormalizeDouble(openPrice + InpStopLoss * _Point, _Digits);
if(trade.PositionModify(ticket, newSL, currentTP))
modified++;
}
}
}
Print("固定止损设置完成,已修改 ", modified, " 个仓位,止损点数: ", InpStopLoss);
}
//+------------------------------------------------------------------+
//| 移动止盈 |
//+------------------------------------------------------------------+
void TrailingStop()
{
int total = PositionsTotal();
double ask = SymbolInfoDouble(_Symbol, SYMBOL_ASK);
double bid = SymbolInfoDouble(_Symbol, SYMBOL_BID);
for(int i = 0; i < total; i++)
{
ulong ticket = PositionGetTicket(i);
if(ticket > 0 && PositionSelectByTicket(ticket))
{
if(PositionGetString(POSITION_SYMBOL) == _Symbol)
{
double openPrice = PositionGetDouble(POSITION_PRICE_OPEN);
double currentSL = PositionGetDouble(POSITION_SL);
double currentTP = PositionGetDouble(POSITION_TP);
ENUM_POSITION_TYPE type = (ENUM_POSITION_TYPE)PositionGetInteger(POSITION_TYPE);
if(type == POSITION_TYPE_BUY)
{
double profit = bid - openPrice;
if(profit >= InpTrailStart * _Point)
{
double newSL = NormalizeDouble(bid - InpTrailDistance * _Point, _Digits);
if(newSL > currentSL || currentSL == 0.0)
{
trade.PositionModify(ticket, newSL, currentTP);
}
}
}
else if(type == POSITION_TYPE_SELL)
{
double profit = openPrice - ask;
if(profit >= InpTrailStart * _Point)
{
double newSL = NormalizeDouble(ask + InpTrailDistance * _Point, _Digits);
if(newSL < currentSL || currentSL == 0.0)
{
trade.PositionModify(ticket, newSL, currentTP);
}
}
}
}
}
}
}
//+------------------------------------------------------------------+
//| 一键保本 |
//+------------------------------------------------------------------+
void SetBreakEven()
{
int modified = 0;
int total = PositionsTotal();
double ask = SymbolInfoDouble(_Symbol, SYMBOL_ASK);
double bid = SymbolInfoDouble(_Symbol, SYMBOL_BID);
for(int i = 0; i < total; i++)
{
ulong ticket = PositionGetTicket(i);
if(ticket > 0 && PositionSelectByTicket(ticket))
{
if(PositionGetString(POSITION_SYMBOL) == _Symbol)
{
double openPrice = PositionGetDouble(POSITION_PRICE_OPEN);
double currentTP = PositionGetDouble(POSITION_TP);
ENUM_POSITION_TYPE type = (ENUM_POSITION_TYPE)PositionGetInteger(POSITION_TYPE);
double newSL = 0.0;
if(type == POSITION_TYPE_BUY)
{
newSL = NormalizeDouble(openPrice + InpBreakEvenPoints * _Point, _Digits);
if(bid > newSL)
{
if(trade.PositionModify(ticket, newSL, currentTP))
modified++;
}
}
else if(type == POSITION_TYPE_SELL)
{
newSL = NormalizeDouble(openPrice - InpBreakEvenPoints * _Point, _Digits);
if(ask < newSL)
{
if(trade.PositionModify(ticket, newSL, currentTP))
modified++;
}
}
}
}
}
Print("一键保本完成,已修改 ", modified, " 个仓位,加点数: ", InpBreakEvenPoints);
}
//+------------------------------------------------------------------+作品说明
已添加顺势加仓和逆势加仓功能,可设置加仓手数倍数、间距点数和最大加仓次数。
使用说明
- 安装方法:将文件保存到 MT5 的 Experts 文件夹,编译后拖到图表上启用。
- 面板拖动:鼠标按住标题栏(≡ 交易控制面板)可拖动面板到任意位置。
- 顺势加仓:盈利达到设定点数后点击"顺势加仓",按手数倍数追加仓位,适合趋势行情。
- 逆势加仓:浮亏达到设定点数后点击"逆势加仓",摊平成本补仓,适合震荡行情(高风险)。
- 加仓参数:手数倍数默认 1.5 倍,顺势间距 200 点,逆势间距 300 点,可在输入参数中调整。
- 风险提示:逆势加仓会放大亏损风险,建议配合严格止损使用;加仓次数不限,需人工控制。
- 移动止盈:自动运行,盈利达到触发点后止损会跟随价格移动保护利润。
- 注意事项:加仓按钮为手动触发,每次点击会检查所有符合条件的持仓并加仓,避免重复点击。
AI 解读根据源码逐行分析写成,供参考,以实际运行为准
这是什么
这是一个挂在 MT5 图表上的图形化交易面板 EA,把开仓、平仓、加仓、改止盈止损这些操作做成按钮,点一下就执行,省得每次右键菜单找半天。面板可以鼠标拖着走,不挡 K 线。
逻辑原理
开仓:点「开多」或「开空」按当前设置的手数下单,价格按市价成交(多单用 Ask,空单用 Bid)。如果参数里勾了自动设 TP/SL,开仓时会按固定点数算好止盈止损一起挂上。
加仓:分两种,都是手动点按钮触发。点「顺势加仓」时,代码会遍历当前品种所有持仓,如果某单已经盈利超过设定点数(多单现价高于开仓价 + 顺势间距,空单反之),就按「上一单手数 × 手数倍数」再开一单同方向的。点「逆势加仓」时,逻辑反过来:如果某单浮亏超过逆势间距点数,就补仓摊平成本。加仓手数上限受最大手数参数约束。
平仓:「一键平仓」关所有当前品种持仓,「一键平多」只关多单,「一键平空」只关空单。
批量止盈止损:点「设置止盈」会给所有持仓按固定点数统一加 TP,点「设置止损」同理加 SL。点「一键保本」会把所有持仓的止损改到开仓价往盈利方向加几个点的位置(多单止损挪到开仓价上方,空单挪到下方)。
移动止盈:如果参数里开了,EA 会每秒检查一次持仓。当某单盈利达到触发点数后,止损会自动跟着价格往盈利方向移动,保持距离固定点数。多单价格涨,止损往上推;空单价格跌,止损往下推。
面板上实时显示当前持仓数量、多空单分布、总盈亏。
参数说明
- InpLotSize(0.01):默认开仓手数,面板上可以用 +/- 按钮临时调整。
- InpLotStep(0.01):每次点 +/- 增减的手数步长。
- InpMinLot(0.01)/ InpMaxLot(10.0):手数调节和加仓的上下限。
- InpEnableAddPosition(true):是否启用加仓功能,关掉后加仓按钮点了也不执行。
- InpAddLotMultiplier(1.5):加仓手数倍数,比如上一单 0.1 手,加仓就开 0.15 手。
- InpTrendAddDistance(200):顺势加仓触发间距,单位点(不是点子),盈利超过这个值才能加。
- InpCounterAddDistance(300):逆势加仓触发间距,浮亏超过这个值才补仓。
- InpMaxAddTimes(3):代码里定义了但未实际限制加仓次数,目前每次点按钮会检查所有符合条件的单子。
- InpTakeProfit(500)/ InpStopLoss(300):固定止盈 / 止损点数,设 0 表示不自动加。
- InpAutoSetTPSL(true):开仓时是否自动按上面两个参数设 TP/SL。
- InpEnableTrailing(true):是否启用移动止盈。
- InpTrailStart(300):移动止盈触发点数,盈利达到这个值止损才开始跟。
- InpTrailDistance(150):移动止盈距离,止损跟价格保持这个点数。
- InpBreakEvenPoints(50):保本时止损放在开仓价上方 / 下方多少点。
- InpPanelX(20)/ InpPanelY(30):面板初始位置坐标,装上后可以拖着调。
- InpCloseColor / InpOpenColor / InpAddColor / InpSetColor:按钮颜色,纯美化用。
怎么用
把文件保存到 MetaTrader 5/MQL5/Experts/ 文件夹,打开 MetaEditor 编译,然后从导航器拖到任意品种的图表上。弹出的设置窗口里调好参数,勾选「允许 EA 交易」和「允许 DLL 导入」(虽然这个 EA 没用 DLL,但有些券商要求勾),点确定。
面板会出现在图表左上角,按住标题栏(≡ 交易控制面板)可以拖到顺手的位置。用 +/- 调好手数,点「开多」或「开空」下单。加仓按钮是手动触发的,想顺势加就点「顺势加仓」,想补仓就点「逆势加仓」,每次点击会检查所有持仓里符合间距条件的单子一起加。平仓、改 TP/SL、保本按钮同理,点了立即执行。
移动止盈是后台自动跑的,开着就行,不用管。面板下方会实时显示持仓情况和盈亏。
适用场景
适合需要频繁手动开平仓的交易者,特别是盘中做短线、需要快速调整止盈止损的场景。顺势加仓适合趋势行情,盈利后追加仓位放大利润;逆势加仓适合震荡行情补仓降成本,但风险大,只建议在有明确支撑阻力位、确定不会单边的时候用。
移动止盈适合单边走势,能锁住大部分利润;保本功能适合行情走了一段、想先保住本金再看后续。
品种和周期没限制,但点数相关的参数要根据波动调。比如黄金(XAUUSD)波动大,顺势间距 200 点可能几分钟就到;欧美(EURUSD)波动小,200 点可能要等半天。建议根据品种平均波动和自己交易周期重新测一遍参数。
风险提示
加仓无次数限制:虽然参数里有 InpMaxAddTimes,但代码里并没有真正限制加仓次数。每次点按钮都会遍历所有持仓,符合条件的全加,连续点或者行情持续单边会一直开新单,手数按倍数增长,保证金和风险暴涨。
逆势加仓高风险:震荡行情里逆势补仓能摊平成本,但如果遇到单边行情(比如非农数据、央行决议),价格不回头,逆势加仓会越亏越多、越补越大,很容易爆仓。建议配合严格的总止损或者只在明确的区间震荡里用。
移动止盈可能过早止损:触发点和距离设太小,价格稍微回调就被扫出来,错过后面的大行情。设太大又起不到保护作用。需要根据品种波动和策略调试。
滑点和成交:市价单在快速行情或流动性差的时候滑点可能比较大,实际成交价和预期有偏差。代码里设了 10 点的偏离容忍(trade.SetDeviationInPoints(10)),超过这个可能下单失败。
手数倍增:加仓倍数 1.5 看着不大,但几次下来手数增长很快。0.01 → 0.015 → 0.023 → 0.035,四次加仓总手数就翻了 3 倍多,保证金占用和单跳盈亏都是指数级上升。
无持仓时间和价格检查:代码只检查盈亏点数,不管仓位开了多久、当前是什么行情。比如开仓后价格剧烈波动来回扫,可能反复触发加仓条件。
常见问题
- 加仓会自动执行吗?
- 不会。顺势加仓和逆势加仓都是点按钮手动触发,每次点击会检查所有持仓里符合间距条件的单子一起加仓。
- 加仓最多能加几次?
- 代码里虽然有 InpMaxAddTimes 参数,但实际没有限制次数。每次点按钮,所有符合盈亏间距条件的持仓都会加仓,需要自己控制点击次数。
- 逆势加仓适合什么情况?
- 适合明确的区间震荡行情,有支撑阻力位、价格来回走的时候。单边趋势行情千万别用,会越补越亏。
- 移动止盈会重绘吗?
- 不会重绘。移动止盈是实时修改订单的止损价格,修改后就生效,不会因为价格回调而撤销已经推上去的止损。
- 这个 EA 能用在 MT4 吗?
- 不能。代码是 MQL5 写的,用了 MT5 的 CTrade 类和持仓系统,MT4 架构不同无法直接运行。
MT5 Trading Control Panel EA
A draggable trading panel for MT5 with one-click open/close, trend/counter-trend position adding, batch TP/SL modification, trailing stop, and break-even functions. Designed for manual traders who need quick order execution.
Source code is shown above. Compiled EX5 files are available to VIP members.
评论
还没有人评论,来抢个沙发。