|
|
//+------------------------------------------------------------------+
//| TrendDSEMA_EA_v3.0.mq4 |
//| Copyright 2023, Automated Logic |
//| For User Request |
//+------------------------------------------------------------------+
#property copyright "Custom EA based on TrendDSEMA - Pyramiding"
#property link ""
#property version "3.00"
#property strict
//--- 输入参数
input string _Comment1 = "=== 交易设置 ===";
input double InpLots = 0.01; // 首单及加仓手数
input int InpMagic = 88888; // 魔术棒号
input int InpSlippage = 5; // 滑点
input string _Comment2 = "=== 顺势加仓设置(盈利加仓) ===";
input int InpGridStep = 200; // 加仓间隔 (点数)
input int InpMaxOrders = 300; // 最大加仓次数 (做多/做空各300次)
input string _Comment3 = "=== 止盈止损设置 ===";
input int InpStopLoss = 5000; // 单单独立止损 (点数)
input int InpTakeProfit = 9000; // 单单独立止盈 (点数)
input string _Comment4 = "=== 盈利保护(保本)设置 ===";
input int InpBreakEven = 160; // 黄金点差保护 (点数),大于此盈利后触发保本
input int InpBE_Add = 10; // 保本时额外加的点数
input string _Comment5 = "=== 单单独立利润回撤设置 ===";
input bool InpUsePointRetrace = true; // 是否启用点数回撤平仓
input int InpRetraceStart = 200; // 启动监控的最小盈利点数 (建议>点差)
input double InpRetracePct = 30.0; // 利润回撤百分比 (30%)
input string _Comment6 = "=== 指标参数 (需与指标一致) ===";
input string InpIndiName = "TrendDSEMA"; // 指标文件名 (必须准确)
input int InpTrendPeriod = 20; // Trend period
input double InpSmooth = 3; // Smoothing period
input double InpTriggerUp = 0.05; // Trigger up level
input double InpTriggerDn = -0.05; // Trigger down level
//--- 结构体:用于记录每张单子的历史最高盈利点数
struct OrderMonitor {
int ticket;
double maxPoints;
};
OrderMonitor g_monitors[]; // 动态数组存储监控信息
//+------------------------------------------------------------------+
//| Expert initialization function |
//+------------------------------------------------------------------+
int OnInit()
{
ArrayResize(g_monitors, 0);
return(INIT_SUCCEEDED);
}
//+------------------------------------------------------------------+
//| Expert tick function |
//+------------------------------------------------------------------+
void OnTick()
{
// 1. 获取指标信号 (取1号柱)
double signal = iCustom(NULL, 0, InpIndiName, InpTrendPeriod, InpSmooth, InpTriggerUp, InpTriggerDn, 5, 1);
// 2. 统计持仓 (获取最后一单的价格)
int buyCount = 0;
int sellCount = 0;
double lastBuyPrice = 0;
double lastSellPrice = 0;
CountOrders(buyCount, sellCount, lastBuyPrice, lastSellPrice);
// 3. 多单逻辑 (LimeGreen)
if (signal == 1.0)
{
// 首单
if (buyCount == 0)
{
OpenOrder(OP_BUY);
}
// 顺势加仓:当前价格比上一单高出 GridStep 点
else if (buyCount < InpMaxOrders)
{
// 修改为:(当前买价 - 上次买入价) >= 间隔
if (Ask - lastBuyPrice >= InpGridStep * Point)
{
OpenOrder(OP_BUY);
}
}
}
// 4. 空单逻辑 (Red)
if (signal == -1.0)
{
// 首单
if (sellCount == 0)
{
OpenOrder(OP_SELL);
}
// 顺势加仓:当前价格比上一单低出 GridStep 点
else if (sellCount < InpMaxOrders)
{
// 修改为:(上次卖出价 - 当前卖价) >= 间隔
if (lastSellPrice - Bid >= InpGridStep * Point)
{
OpenOrder(OP_SELL);
}
}
}
// 5. 盈利保护 (保本)
ManageBreakEven();
// 6. 单单独立利润回撤清仓
if (InpUsePointRetrace)
{
ManageIndividualRetracement();
}
}
//+------------------------------------------------------------------+
//| 开仓函数 |
//+------------------------------------------------------------------+
void OpenOrder(int type)
{
double sl = 0, tp = 0;
double price = (type == OP_BUY) ? Ask : Bid;
if (InpStopLoss > 0)
sl = (type == OP_BUY) ? NormalizeDouble(price - InpStopLoss * Point, Digits) : NormalizeDouble(price + InpStopLoss * Point, Digits);
if (InpTakeProfit > 0)
tp = (type == OP_BUY) ? NormalizeDouble(price + InpTakeProfit * Point, Digits) : NormalizeDouble(price - InpTakeProfit * Point, Digits);
int ticket = OrderSend(Symbol(), type, InpLots, price, InpSlippage, sl, tp, "TrendDSEMA EA", InpMagic, 0, (type == OP_BUY ? clrGreen : clrRed));
if (ticket < 0) Print("开单失败: ", GetLastError());
}
//+------------------------------------------------------------------+
//| 统计订单 (寻找最新一单的开仓价) |
//+------------------------------------------------------------------+
void CountOrders(int &bCount, int &sCount, double &lBuyPrice, double &lSellPrice)
{
bCount = 0; sCount = 0; lBuyPrice = 0; lSellPrice = 0;
datetime lastBuyTime = 0;
datetime lastSellTime = 0;
for (int i = 0; i < OrdersTotal(); i++)
{
if (OrderSelect(i, SELECT_BY_POS, MODE_TRADES))
{
if (OrderSymbol() == Symbol() && OrderMagicNumber() == InpMagic)
{
if (OrderType() == OP_BUY)
{
bCount++;
// 寻找开仓时间最晚的那一单
if (OrderOpenTime() > lastBuyTime) {
lastBuyTime = OrderOpenTime();
lBuyPrice = OrderOpenPrice();
}
}
if (OrderType() == OP_SELL)
{
sCount++;
// 寻找开仓时间最晚的那一单
if (OrderOpenTime() > lastSellTime) {
lastSellTime = OrderOpenTime();
lSellPrice = OrderOpenPrice();
}
}
}
}
}
}
//+------------------------------------------------------------------+
//| 保本逻辑 |
//+------------------------------------------------------------------+
void ManageBreakEven()
{
for (int i = 0; i < OrdersTotal(); i++)
{
if (OrderSelect(i, SELECT_BY_POS, MODE_TRADES))
{
if (OrderSymbol() == Symbol() && OrderMagicNumber() == InpMagic)
{
double openPrice = OrderOpenPrice();
if (OrderType() == OP_BUY)
{
if (Bid - openPrice > InpBreakEven * Point)
{
double newSL = NormalizeDouble(openPrice + InpBE_Add * Point, Digits);
if (OrderStopLoss() < newSL || OrderStopLoss() == 0)
OrderModify(OrderTicket(), openPrice, newSL, OrderTakeProfit(), 0, clrBlue);
}
}
if (OrderType() == OP_SELL)
{
if (openPrice - Ask > InpBreakEven * Point)
{
double newSL = NormalizeDouble(openPrice - InpBE_Add * Point, Digits);
if (OrderStopLoss() > newSL || OrderStopLoss() == 0)
OrderModify(OrderTicket(), openPrice, newSL, OrderTakeProfit(), 0, clrBlue);
}
}
}
}
}
}
//+------------------------------------------------------------------+
//| 单单独立利润回撤清仓 (基于点数) |
//+------------------------------------------------------------------+
void ManageIndividualRetracement()
{
for (int i = OrdersTotal() - 1; i >= 0; i--)
{
if (OrderSelect(i, SELECT_BY_POS, MODE_TRADES))
{
if (OrderSymbol() == Symbol() && OrderMagicNumber() == InpMagic)
{
int ticket = OrderTicket();
double currentPoints = 0;
// 1. 计算当前单子的盈利点数
if (OrderType() == OP_BUY)
currentPoints = (Bid - OrderOpenPrice()) / Point;
else if (OrderType() == OP_SELL)
currentPoints = (OrderOpenPrice() - Ask) / Point;
else
continue;
// 2. 在数组中查找或注册该订单
int index = -1;
for (int k = 0; k < ArraySize(g_monitors); k++)
{
if (g_monitors[k].ticket == ticket)
{
index = k;
break;
}
}
// 如果是新订单,加入数组
if (index == -1)
{
int s = ArraySize(g_monitors);
ArrayResize(g_monitors, s + 1);
g_monitors[s].ticket = ticket;
g_monitors[s].maxPoints = currentPoints; // 初始最高点
index = s;
}
// 3. 更新最高盈利点数
if (currentPoints > g_monitors[index].maxPoints)
{
g_monitors[index].maxPoints = currentPoints;
}
// 4. 判断回撤平仓条件
// 必须先满足最低启动点数(InpRetraceStart),防止刚开单震荡就被平
if (g_monitors[index].maxPoints >= InpRetraceStart)
{
double limitPoints = g_monitors[index].maxPoints * (1.0 - InpRetracePct / 100.0);
if (currentPoints < limitPoints)
{
Print("触发单单独立回撤平仓! 订单号:", ticket, " 最高点数:", g_monitors[index].maxPoints, " 当前点数:", currentPoints, " 阈值:", limitPoints);
bool res = false;
if (OrderType() == OP_BUY)
res = OrderClose(ticket, OrderLots(), Bid, InpSlippage, clrYellow);
else
res = OrderClose(ticket, OrderLots(), Ask, InpSlippage, clrYellow);
if (!res) Print("回撤平仓失败: ", GetLastError());
}
}
}
}
}
CleanUpMonitorArray();
}
//+------------------------------------------------------------------+
//| 清理监控数组 |
//+------------------------------------------------------------------+
void CleanUpMonitorArray()
{
for (int i = ArraySize(g_monitors) - 1; i >= 0; i--)
{
if (!OrderSelect(g_monitors[i].ticket, SELECT_BY_TICKET) || OrderCloseTime() > 0)
{
RemoveFromArray(i);
}
}
}
//+------------------------------------------------------------------+
//| 辅助函数:移除数组元素 |
//+------------------------------------------------------------------+
void RemoveFromArray(int index)
{
int size = ArraySize(g_monitors);
if (index < size - 1)
{
g_monitors[index] = g_monitors[size - 1];
}
ArrayResize(g_monitors, size - 1);
}
//+------------------------------------------------------------------+
|
|