|
//+------------------------------------------------------------------+
//| Grid_Reversal_EA.mq5 |
//| Copyright 2024, GPT-4 for MQL5 |
//| https://www.google.com |
//+------------------------------------------------------------------+
#property copyright "Copyright 2024, GPT-4 for MQL5"
#property link "https://www.google.com"
#property version "1.40"
#property description "带时间控制和整体止损的圆点指标EA"
#include <Trade/Trade.mqh>
//--- EA 输入参数
input group "指标参数"
input int inpFastLength = 6; // 圆点指标 - Fast Length
input int inpSlowLength = 14; // 圆点指标 - Slow Length
input string indicator_name = "圆点指标"; // 指标文件名称
input group "订单参数"
input double initial_lots = 0.01; // 初始/加仓订单手数
input ulong magic_number = 12345; // EA魔术数字
input double reversal_multiplier = 2.0; // 反转加倍乘数
input group "顺势加仓参数"
input int grid_distance_points = 1000; // 挂单间隔 (点)
input int max_pending_orders = 20; // 最大挂单数量
input group "盈利管理参数"
input double profit_target_usd = 5.0; // 整体盈利目标(美元)
input double profit_drawdown_usd = 3.0; // 盈利回撤(美元)
input group "【新】亏损管理参数"
input bool EnableBasketStopLoss = true; // 启用整体止损
input double BasketStopLossUSD = -200.0; // 整体止损金额 (美元, 请使用负数)
input group "时间控制参数"
input int StartHour = 0; // 开单开始时间 (小时)
input int EndHour = 14; // 开单结束时间 (小时, 14点代表0:00-13:59)
input bool EnableDailyClose= true; // 启用每日清仓
input int CloseHour = 14; // 每日清仓时间 (小时)
input bool Monday = true; // 周一开单
input bool Tuesday = true; // 周二开单
input bool Wednesday = true; // 周三开单
input bool Thursday = true; // 周四开单
input bool Friday = true; // 周五开单
input bool Saturday = true; // 周六开单
input bool Sunday = true; // 周日开单
//--- 全局变量
CTrade trade;
int indicator_handle;
string ea_comment = "PyramidEA_TS";
//--- 用于跟踪状态的静态变量
enum TrendDirection
{
NONE,
UP,
DOWN
};
static TrendDirection current_trend = NONE;
static double max_profit = 0.0;
static datetime last_close_time = 0; // 用于跟踪每日清仓
//+------------------------------------------------------------------+
//| EA初始化函数 |
//+------------------------------------------------------------------+
int OnInit()
{
trade.SetExpertMagicNumber(magic_number);
trade.SetMarginMode();
trade.SetTypeFillingBySymbol(_Symbol);
indicator_handle = iCustom(_Symbol, _Period, indicator_name, inpFastLength, inpSlowLength);
if(indicator_handle == INVALID_HANDLE)
{
Alert("错误: 无法加载圆点指标. 请确保 '", indicator_name, ".ex5' 在 MQL5/Indicators 文件夹中。");
return(INIT_FAILED);
}
InitializeTrendState();
Print("EA初始化成功. 魔术数字: ", magic_number, ". 风险控制已启用.");
return(INIT_SUCCEEDED);
}
//+------------------------------------------------------------------+
//| EA启动时,检查现有仓位以确定当前趋势状态 |
//+------------------------------------------------------------------+
void InitializeTrendState()
{
int buys = 0;
int sells = 0;
for(int i = PositionsTotal() - 1; i >= 0; i--)
{
if(PositionGetSymbol(i) == _Symbol && PositionGetInteger(POSITION_MAGIC) == magic_number)
{
if(PositionGetInteger(POSITION_TYPE) == POSITION_TYPE_BUY)
buys++;
else if(PositionGetInteger(POSITION_TYPE) == POSITION_TYPE_SELL)
sells++;
}
}
if(buys > 0 && sells == 0) current_trend = UP;
else if(sells > 0 && buys == 0) current_trend = DOWN;
else current_trend = NONE;
Print("EA启动,检测到当前趋势状态: ", EnumToString(current_trend));
}
//+------------------------------------------------------------------+
//| EA去初始化函数 |
//+------------------------------------------------------------------+
void OnDeinit(const int reason)
{
if(indicator_handle != INVALID_HANDLE) IndicatorRelease(indicator_handle);
Print("EA已停止。原因代码: ", reason);
}
//+------------------------------------------------------------------+
//| EA核心逻辑 - OnTick |
//+------------------------------------------------------------------+
void OnTick()
{
// 盈利平仓检查
if(CheckBasketProfitClosure()) return;
// 【新】亏损平仓检查
if(CheckBasketStopLoss()) return;
// 每日定时清仓检查
CheckDailyClose();
// 新K线信号检查
if(IsNewBar())
{
CheckSignal();
}
}
//+------------------------------------------------------------------+
//| 每日定时清仓 |
//+------------------------------------------------------------------+
void CheckDailyClose()
{
if(!EnableDailyClose) return;
MqlDateTime dt;
TimeCurrent(dt);
if(dt.hour == CloseHour)
{
MqlDateTime last_dt;
TimeToStruct(last_close_time, last_dt);
if(last_dt.day_of_year != dt.day_of_year || last_dt.year != dt.year)
{
Print("每日清仓时间到达 (", IntegerToString(CloseHour), ":00). 平仓所有订单...");
CloseAllTrades();
last_close_time = TimeCurrent(dt);
}
}
}
//+------------------------------------------------------------------+
//| 检查是否在允许的交易时间内 |
//+------------------------------------------------------------------+
bool IsTradingAllowed()
{
MqlDateTime dt;
TimeCurrent(dt);
bool day_allowed = false;
switch(dt.day_of_week)
{
case 0: day_allowed = Sunday; break;
case 1: day_allowed = Monday; break;
case 2: day_allowed = Tuesday; break;
case 3: day_allowed = Wednesday; break;
case 4: day_allowed = Thursday; break;
case 5: day_allowed = Friday; break;
case 6: day_allowed = Saturday; break;
}
bool hour_allowed = (dt.hour >= StartHour && dt.hour < EndHour);
return day_allowed && hour_allowed;
}
//+------------------------------------------------------------------+
//| 检查新K线 |
//+------------------------------------------------------------------+
bool IsNewBar()
{
static datetime last_bar_time = 0;
datetime current_bar_time = (datetime)SeriesInfoInteger(_Symbol, _Period, SERIES_LASTBAR_DATE);
if(last_bar_time != current_bar_time)
{
last_bar_time = current_bar_time;
return true;
}
return false;
}
//+------------------------------------------------------------------+
//| 检查指标信号 |
//+------------------------------------------------------------------+
void CheckSignal()
{
if(!IsTradingAllowed()) return;
double arrow_price_buffer[], arrow_color_buffer[];
if(CopyBuffer(indicator_handle, 7, 1, 2, arrow_price_buffer) < 2 ||
CopyBuffer(indicator_handle, 8, 1, 2, arrow_color_buffer) < 2)
{
Print("错误: 无法从指标复制数据. Error code: ", GetLastError());
return;
}
double arrow_value = arrow_price_buffer[0], color_value = arrow_color_buffer[0];
if(arrow_value != EMPTY_VALUE)
{
if(color_value == 0) // 多头信号
{
if(current_trend == DOWN) HandleReversal(UP);
else if(current_trend == NONE)
{
if(trade.Buy(initial_lots, _Symbol, 0, 0, 0, ea_comment))
{
current_trend = UP;
PlacePyramidGrid(UP);
}
}
}
else if(color_value == 1) // 空头信号
{
if(current_trend == UP) HandleReversal(DOWN);
else if(current_trend == NONE)
{
if(trade.Sell(initial_lots, _Symbol, 0, 0, 0, ea_comment))
{
current_trend = DOWN;
PlacePyramidGrid(DOWN);
}
}
}
}
}
//+------------------------------------------------------------------+
//| 处理反转逻辑 |
//+------------------------------------------------------------------+
void HandleReversal(TrendDirection new_trend)
{
double buy_lots = 0, sell_lots = 0;
for(int i = PositionsTotal() - 1; i >= 0; i--)
{
if(PositionGetSymbol(i) == _Symbol && PositionGetInteger(POSITION_MAGIC) == magic_number)
{
if(PositionGetInteger(POSITION_TYPE) == POSITION_TYPE_BUY) buy_lots += PositionGetDouble(POSITION_VOLUME);
else sell_lots += PositionGetDouble(POSITION_VOLUME);
}
}
double lot_diff = MathAbs(buy_lots - sell_lots);
double new_trade_lots = NormalizeDouble(lot_diff * reversal_multiplier, 2);
double min_lots = SymbolInfoDouble(_Symbol, SYMBOL_VOLUME_MIN);
if(new_trade_lots < min_lots) new_trade_lots = min_lots;
if(new_trend == UP)
{
DeleteAllPendingSells();
if(trade.Buy(new_trade_lots, _Symbol, 0, 0, 0, ea_comment))
{
current_trend = UP;
PlacePyramidGrid(UP);
}
}
else if(new_trend == DOWN)
{
DeleteAllPendingBuys();
if(trade.Sell(new_trade_lots, _Symbol, 0, 0, 0, ea_comment))
{
current_trend = DOWN;
PlacePyramidGrid(DOWN);
}
}
}
//+------------------------------------------------------------------+
//| 信号出现后,一次性挂出顺势的突破单网格 |
//+------------------------------------------------------------------+
void PlacePyramidGrid(TrendDirection trend)
{
if(trend == NONE) return;
if(trend == UP)
{
double current_ask = SymbolInfoDouble(_Symbol, SYMBOL_ASK);
for(int i = 1; i <= max_pending_orders; i++)
{
double stop_price = current_ask + (i * grid_distance_points * _Point);
stop_price = NormalizeDouble(stop_price, _Digits);
trade.BuyStop(initial_lots, stop_price, _Symbol, 0, 0, ORDER_TIME_GTC, 0, ea_comment);
}
}
else if(trend == DOWN)
{
double current_bid = SymbolInfoDouble(_Symbol, SYMBOL_BID);
for(int i = 1; i <= max_pending_orders; i++)
{
double stop_price = current_bid - (i * grid_distance_points * _Point);
stop_price = NormalizeDouble(stop_price, _Digits);
trade.SellStop(initial_lots, stop_price, _Symbol, 0, 0, ORDER_TIME_GTC, 0, ea_comment);
}
}
}
//+------------------------------------------------------------------+
//| 检查整体盈利平仓条件 |
//+------------------------------------------------------------------+
bool CheckBasketProfitClosure()
{
double current_profit = GetTotalFloatingProfit();
if(current_profit <= 0)
{
max_profit = 0;
return false;
}
if(current_profit > max_profit) max_profit = current_profit;
if(max_profit >= profit_target_usd && current_profit <= max_profit - profit_drawdown_usd)
{
Print("盈利平仓条件触发. Max Profit: ", DoubleToString(max_profit,2), " USD. 平仓所有订单.");
CloseAllTrades();
return true;
}
return false;
}
//+------------------------------------------------------------------+
//| 【新】检查整体亏损平仓条件 |
//+------------------------------------------------------------------+
bool CheckBasketStopLoss()
{
if (!EnableBasketStopLoss) return false;
double current_profit = GetTotalFloatingProfit();
if (current_profit <= BasketStopLossUSD)
{
Print("整体止损触发. 当前亏损: ", DoubleToString(current_profit, 2), " USD. 止损线: ", DoubleToString(BasketStopLossUSD, 2), " USD. 平仓所有订单.");
CloseAllTrades();
return true;
}
return false;
}
//+------------------------------------------------------------------+
//| 计算当前交易品种和魔术数字的总浮动盈亏 |
//+------------------------------------------------------------------+
double GetTotalFloatingProfit()
{
double total_profit = 0;
for(int i = PositionsTotal() - 1; i >= 0; i--)
{
if(PositionGetSymbol(i) == _Symbol && PositionGetInteger(POSITION_MAGIC) == magic_number)
{
total_profit += PositionGetDouble(POSITION_PROFIT) + PositionGetDouble(POSITION_SWAP);
}
}
return total_profit;
}
//+------------------------------------------------------------------+
//| 关闭所有持仓单和挂单 |
//+------------------------------------------------------------------+
void CloseAllTrades()
{
for(int i = PositionsTotal() - 1; i >= 0; i--)
{
if(PositionGetSymbol(i) == _Symbol && PositionGetInteger(POSITION_MAGIC) == magic_number)
trade.PositionClose(PositionGetTicket(i));
}
for(int i = OrdersTotal() - 1; i >= 0; i--)
{
ulong ticket = OrderGetTicket(i);
if(ticket > 0)
{
if(OrderGetString(ORDER_SYMBOL) == _Symbol && OrderGetInteger(ORDER_MAGIC) == magic_number)
trade.OrderDelete(ticket);
}
}
current_trend = NONE;
max_profit = 0.0;
Print("所有仓位已清理完毕。");
}
//+------------------------------------------------------------------+
//| Helper Functions (辅助函数) |
//+------------------------------------------------------------------+
void DeleteAllPendingBuys()
{
for(int i = OrdersTotal() - 1; i >= 0; i--)
{
ulong ticket = OrderGetTicket(i);
if(ticket > 0)
{
if(OrderGetString(ORDER_SYMBOL) == _Symbol &&
OrderGetInteger(ORDER_MAGIC) == magic_number &&
OrderGetInteger(ORDER_TYPE) == ORDER_TYPE_BUY_STOP)
{
trade.OrderDelete(ticket);
}
}
}
}
void DeleteAllPendingSells()
{
for(int i = OrdersTotal() - 1; i >= 0; i--)
{
ulong ticket = OrderGetTicket(i);
if(ticket > 0)
{
if(OrderGetString(ORDER_SYMBOL) == _Symbol &&
OrderGetInteger(ORDER_MAGIC) == magic_number &&
OrderGetInteger(ORDER_TYPE) == ORDER_TYPE_SELL_STOP)
{
trade.OrderDelete(ticket);
}
}
}
}
|
|