1905 lines
85 KiB
MQL5
1905 lines
85 KiB
MQL5
//+------------------------------------------------------------------+
|
|
//| XAUUSD_Dual_Martin_EA.mq5 |
|
|
//| Copyright 2025 |
|
|
//| |
|
|
//+------------------------------------------------------------------+
|
|
#property copyright "Copyright 2024"
|
|
#property link ""
|
|
#property version "1.00"
|
|
#property description "XAUUSD双向马丁EA - 多空双向马丁策略,空方和多方仓位分开管理"
|
|
|
|
#include <Trade\Trade.mqh>
|
|
CTrade trade;
|
|
|
|
//+------------------------------------------------------------------+
|
|
//| 输入参数 |
|
|
//+------------------------------------------------------------------+
|
|
input group "=== 基本设置 ==="
|
|
input ulong Magic_Number = 999002; // EA魔术号码
|
|
input string EA_Comment = "XAUUSD_Dual_Martin"; // 订单注释
|
|
|
|
input group "=== 策略参数 ==="
|
|
input double Initial_Lots = 0.04; // 初始入场手数
|
|
input double Price_Threshold = 0.4; // 价格变动阈值(美元)
|
|
input double Martingale_Multiplier = 1.5; // 马丁格尔倍数
|
|
input double Profit_Target = 4; // 盈利目标(美元)
|
|
input double Profit_Protection_Ratio = 0.9; // 盈利保护比例(0.1-1.0)
|
|
input double Large_Position_Threshold = 0.8; // 大仓位阈值(手数)
|
|
input double Price_Track_Distance = 0.6; // 价格追踪距离(美元)
|
|
input bool Enable_Long_Martin = true; // 是否开启多方马丁
|
|
input bool Enable_Short_Martin = true; // 是否开启空方马丁
|
|
input bool Use_Async_Close = true; // 是否使用异步提交平仓
|
|
input int Async_Close_Max_Retries = 1; // 异步平仓最大重试次数
|
|
input int Async_Close_Retry_Seconds = 1; // 异步平仓重试间隔秒
|
|
input int Async_Close_Timeout_Seconds = 2; // 异步平仓超时秒,超时后切换同步
|
|
|
|
input group "=== ATR设置 ==="
|
|
input int ATR_Period = 2; // ATR周期
|
|
input double ATR_Threshold = 0.6; // ATR阈值(美元)
|
|
input double ATR_Distance_Multiplier = 0.9;// ATR加仓距离系数
|
|
|
|
input group "=== ADX趋势过滤 ==="
|
|
input bool Enable_ADX_Filter = true; // 启用ADX趋势过滤
|
|
input int ADX_Period = 20; // ADX周期
|
|
input double ADX_Threshold = 30.0; // ADX阈值,大于此值停止逆势加仓
|
|
|
|
input group "=== DEMA风险控制 ==="
|
|
input bool Enable_DEMA_Risk_Control = true; // 启用DEMA风险控制
|
|
input int DEMA_Short_Period = 30; // DEMA短周期
|
|
input int DEMA_Long_Period = 120; // DEMA长周期
|
|
input double DEMA_Risk_Lot_Threshold = 0.8; // DEMA风险控制手数阈值
|
|
input double DEMA_Difference_Threshold = 0.01; // DEMA差值恢复阈值
|
|
input bool Enable_DEMA_Status_Log = true; // 启用DEMA状态日志(建议关闭以减少日志)
|
|
|
|
input group "=== 时间过滤 ==="
|
|
input bool Enable_Time_Filter = false; // 启用时间过滤
|
|
input int Trade_Start_Hour = 6; // 交易开始小时
|
|
input int Trade_End_Hour = 22; // 交易结束小时
|
|
|
|
input group "=== 风险控制 ==="
|
|
input bool Enable_Risk_Control = true; // 启用风险控制
|
|
input int Max_Add_Per_Bar = 2; // 单根K线单方向最大加仓次数
|
|
input double Volatile_Bar_Threshold = 8.0; // 剧烈波动K线阈值(美元)
|
|
input int Volatile_Bar_Pause = 3; // 剧烈波动后暂停加仓K线数
|
|
|
|
input group "=== 重启设置 ==="
|
|
input int Restart_Delay_Seconds = 1; // 重启延迟时间(秒)
|
|
|
|
//+------------------------------------------------------------------+
|
|
//| 全局变量 |
|
|
//+------------------------------------------------------------------+
|
|
// 多空方向状态
|
|
bool is_long_martin_active = false; // 多头马丁是否活跃
|
|
bool is_short_martin_active = false; // 空头马丁是否活跃
|
|
|
|
// 多头马丁状态
|
|
double long_last_entry_price = 0.0; // 多头最后入场价格
|
|
double long_current_lot_size = 0.0; // 多头当前手数大小
|
|
double long_trailing_stop_level = 0.0; // 多头追踪止损水平
|
|
double long_highest_profit = 0.0; // 多头最高盈利记录
|
|
int long_martin_add_count = 0; // 多头马丁加仓次数
|
|
bool long_large_position_mode = false; // 多头大仓位模式
|
|
double long_price_track_level = 0.0; // 多头价格追踪水平
|
|
|
|
// 空头马丁状态
|
|
double short_last_entry_price = 0.0; // 空头最后入场价格
|
|
double short_current_lot_size = 0.0; // 空头当前手数大小
|
|
double short_trailing_stop_level = 0.0; // 空头追踪止损水平
|
|
double short_highest_profit = 0.0; // 空头最高盈利记录
|
|
int short_martin_add_count = 0; // 空头马丁加仓次数
|
|
bool short_large_position_mode = false; // 空头大仓位模式
|
|
double short_price_track_level = 0.0; // 空头价格追踪水平
|
|
|
|
// 价格跟踪
|
|
double last_check_price = 0.0; // 上次检查价格
|
|
bool ea_started = false; // EA是否已启动
|
|
|
|
// ATR 句柄
|
|
int atr_handle = INVALID_HANDLE;
|
|
|
|
// ADX 指标句柄
|
|
int adx_handle = INVALID_HANDLE;
|
|
|
|
// DEMA 指标句柄
|
|
int dema_short_handle = INVALID_HANDLE;
|
|
int dema_long_handle = INVALID_HANDLE;
|
|
|
|
// ADX趋势状态
|
|
bool adx_trend_filter_active = false; // ADX趋势过滤是否激活
|
|
bool is_uptrend = false; // 当前是否为上升趋势
|
|
bool is_downtrend = false; // 当前是否为下降趋势
|
|
|
|
// 最近一次加仓距离计算的日志辅助
|
|
bool last_distance_used_atr = false; // 是否使用了ATR
|
|
double last_distance_atr = 0.0; // 最近一次ATR数值
|
|
double last_distance_final = 0.0; // 最近一次计算得到的最终距离
|
|
|
|
// 异步平仓状态
|
|
bool long_async_closing = false; // 多头是否处于异步平仓中
|
|
bool short_async_closing = false; // 空头是否处于异步平仓中
|
|
datetime long_async_start_time = 0; // 多头异步平仓开始时间
|
|
datetime short_async_start_time = 0; // 空头异步平仓开始时间
|
|
int long_async_retries = 0; // 多头异步已重试次数
|
|
int short_async_retries = 0; // 空头异步已重试次数
|
|
datetime long_async_last_retry_time = 0; // 多头上次重试时间
|
|
datetime short_async_last_retry_time = 0; // 空头上次重试时间
|
|
|
|
// 风险控制状态
|
|
int long_add_count_this_bar = 0; // 当前K线多头加仓次数
|
|
int short_add_count_this_bar = 0; // 当前K线空头加仓次数
|
|
int current_bar_index = 0; // 当前K线索引
|
|
bool long_pause_after_volatile = false; // 多头因剧烈波动暂停
|
|
bool short_pause_after_volatile = false; // 空头因剧烈波动暂停
|
|
int long_pause_bars_remaining = 0; // 多头暂停剩余K线数
|
|
int short_pause_bars_remaining = 0; // 空头暂停剩余K线数
|
|
|
|
// DEMA风险控制状态
|
|
bool long_dema_risk_active = false; // 多头DEMA风险控制是否激活
|
|
bool short_dema_risk_active = false; // 空头DEMA风险控制是否激活
|
|
bool long_dema_pause = false; // 多头是否因DEMA风险控制暂停
|
|
bool short_dema_pause = false; // 空头是否因DEMA风险控制暂停
|
|
double long_max_lots = 0.0; // 多头最大手数
|
|
double short_max_lots = 0.0; // 空头最大手数
|
|
datetime last_dema_check_time = 0; // 上次DEMA检查时间
|
|
|
|
//+------------------------------------------------------------------+
|
|
//| Expert initialization function |
|
|
//+------------------------------------------------------------------+
|
|
int OnInit() {
|
|
// 设置交易参数
|
|
trade.SetExpertMagicNumber(Magic_Number);
|
|
trade.SetDeviationInPoints(10);
|
|
// 默认使用同步模式开仓;平仓时可按需临时切换到异步
|
|
trade.SetAsyncMode(false);
|
|
if(Use_Async_Close)
|
|
Print("开仓使用同步模式;平仓按设置使用异步提交");
|
|
else
|
|
Print("开仓/平仓均使用同步模式");
|
|
|
|
// 初始化ATR指标
|
|
atr_handle = iATR(_Symbol, _Period, ATR_Period);
|
|
if(atr_handle == INVALID_HANDLE) {
|
|
Print("ATR指标创建失败,周期=", ATR_Period, ", 错误=", GetLastError());
|
|
}
|
|
|
|
// 初始化ADX指标
|
|
adx_handle = iADX(_Symbol, _Period, ADX_Period);
|
|
if(adx_handle == INVALID_HANDLE) {
|
|
Print("ADX指标创建失败,周期=", ADX_Period, ", 错误=", GetLastError());
|
|
}
|
|
|
|
// 初始化DEMA指标
|
|
dema_short_handle = iDEMA(_Symbol, _Period, DEMA_Short_Period, 0, 0);
|
|
if(dema_short_handle == INVALID_HANDLE) {
|
|
Print("DEMA短周期指标创建失败,周期=", DEMA_Short_Period, ", 错误=", GetLastError());
|
|
}
|
|
|
|
dema_long_handle = iDEMA(_Symbol, _Period, DEMA_Long_Period, 0, 0);
|
|
if(dema_long_handle == INVALID_HANDLE) {
|
|
Print("DEMA长周期指标创建失败,周期=", DEMA_Long_Period, ", 错误=", GetLastError());
|
|
}
|
|
|
|
// 初始化变量
|
|
is_long_martin_active = false;
|
|
is_short_martin_active = false;
|
|
|
|
long_last_entry_price = 0.0;
|
|
long_current_lot_size = Initial_Lots;
|
|
long_trailing_stop_level = 0.0;
|
|
long_highest_profit = 0.0;
|
|
long_martin_add_count = 0;
|
|
long_large_position_mode = false;
|
|
long_price_track_level = 0.0;
|
|
|
|
short_last_entry_price = 0.0;
|
|
short_current_lot_size = Initial_Lots;
|
|
short_trailing_stop_level = 0.0;
|
|
short_highest_profit = 0.0;
|
|
short_martin_add_count = 0;
|
|
short_large_position_mode = false;
|
|
short_price_track_level = 0.0;
|
|
|
|
last_check_price = 0.0;
|
|
ea_started = false;
|
|
|
|
// 初始化ADX状态
|
|
adx_trend_filter_active = false;
|
|
is_uptrend = false;
|
|
is_downtrend = false;
|
|
|
|
// 初始化风险控制状态
|
|
long_add_count_this_bar = 0;
|
|
short_add_count_this_bar = 0;
|
|
current_bar_index = Bars(_Symbol, _Period);
|
|
long_pause_after_volatile = false;
|
|
short_pause_after_volatile = false;
|
|
long_pause_bars_remaining = 0;
|
|
|
|
// 初始化DEMA风险控制状态
|
|
long_dema_risk_active = false;
|
|
short_dema_risk_active = false;
|
|
long_dema_pause = false;
|
|
short_dema_pause = false;
|
|
long_max_lots = 0.0;
|
|
short_max_lots = 0.0;
|
|
last_dema_check_time = 0;
|
|
|
|
// 显示时间过滤设置
|
|
if(Enable_Time_Filter) {
|
|
Print("时间过滤:已启用,交易时间 ", Trade_Start_Hour, ":00 - ", Trade_End_Hour, ":00 (服务器时间)");
|
|
} else {
|
|
Print("时间过滤:已禁用,24小时交易");
|
|
}
|
|
|
|
// 显示ADX设置
|
|
if(Enable_ADX_Filter) {
|
|
Print("ADX趋势过滤:已启用,周期=", ADX_Period, ",阈值=", ADX_Threshold);
|
|
} else {
|
|
Print("ADX趋势过滤:已禁用");
|
|
}
|
|
|
|
// 显示DEMA设置
|
|
if(Enable_DEMA_Risk_Control) {
|
|
Print("DEMA风险控制:已启用,短周期=", DEMA_Short_Period, ",长周期=", DEMA_Long_Period, ",手数阈值=", DEMA_Risk_Lot_Threshold, ",差值阈值=", DEMA_Difference_Threshold);
|
|
} else {
|
|
Print("DEMA风险控制:已禁用");
|
|
}
|
|
|
|
Print("XAUUSD双向马丁EA初始化成功");
|
|
Print("大仓位阈值: ", Large_Position_Threshold, "手,价格追踪距离: ", Price_Track_Distance, "美元");
|
|
return(INIT_SUCCEEDED);
|
|
}
|
|
|
|
//+------------------------------------------------------------------+
|
|
//| Expert deinitialization function |
|
|
//+------------------------------------------------------------------+
|
|
void OnDeinit(const int reason) {
|
|
Print("XAUUSD双向马丁EA已停止");
|
|
if(atr_handle != INVALID_HANDLE) {
|
|
IndicatorRelease(atr_handle);
|
|
atr_handle = INVALID_HANDLE;
|
|
}
|
|
if(adx_handle != INVALID_HANDLE) {
|
|
IndicatorRelease(adx_handle);
|
|
adx_handle = INVALID_HANDLE;
|
|
}
|
|
if(dema_short_handle != INVALID_HANDLE) {
|
|
IndicatorRelease(dema_short_handle);
|
|
dema_short_handle = INVALID_HANDLE;
|
|
}
|
|
if(dema_long_handle != INVALID_HANDLE) {
|
|
IndicatorRelease(dema_long_handle);
|
|
dema_long_handle = INVALID_HANDLE;
|
|
}
|
|
}
|
|
|
|
//+------------------------------------------------------------------+
|
|
//| Expert tick function |
|
|
//+------------------------------------------------------------------+
|
|
void OnTick() {
|
|
// 检查风险控制
|
|
CheckRiskControl();
|
|
|
|
// 检查DEMA风险控制
|
|
CheckDEMARiskControl();
|
|
|
|
// 检查是否在允许交易的时间段内
|
|
if(!IsInTradingTime()) {
|
|
return;
|
|
}
|
|
|
|
double current_price = SymbolInfoDouble(_Symbol, SYMBOL_BID);
|
|
|
|
// EA启动时立即开始多空双向入场
|
|
if(!ea_started) {
|
|
StartDualMartin();
|
|
ea_started = true;
|
|
return;
|
|
}
|
|
|
|
// 检查ADX趋势状态
|
|
CheckADXTrend();
|
|
|
|
// 每分钟输出一次马丁状态(包括是否被ADX和DEMA阻止)
|
|
static datetime last_status_output = 0;
|
|
if(TimeCurrent() - last_status_output >= 60) {
|
|
string long_status = "多头马丁状态: ";
|
|
string short_status = "空头马丁状态: ";
|
|
|
|
// 多头状态
|
|
if(!Enable_Long_Martin) {
|
|
long_status += "已禁用";
|
|
} else if(!is_long_martin_active) {
|
|
long_status += "未激活";
|
|
// 检查阻止重启的原因
|
|
if(Enable_ADX_Filter && adx_trend_filter_active && is_downtrend) {
|
|
long_status += "(被ADX阻止)";
|
|
} else if(Enable_DEMA_Risk_Control && long_dema_pause) {
|
|
long_status += "(被DEMA阻止)";
|
|
}
|
|
} else {
|
|
long_status += "活跃";
|
|
// 检查加仓阻止原因
|
|
if(Enable_ADX_Filter && adx_trend_filter_active && is_downtrend) {
|
|
long_status += "(加仓被ADX阻止)";
|
|
} else if(Enable_DEMA_Risk_Control && long_dema_pause) {
|
|
long_status += "(加仓被DEMA阻止)";
|
|
} else if(Enable_Risk_Control && long_add_count_this_bar >= Max_Add_Per_Bar) {
|
|
long_status += "(加仓次数超限)";
|
|
} else if(Enable_Risk_Control && long_pause_after_volatile) {
|
|
long_status += "(剧烈波动暂停)";
|
|
}
|
|
}
|
|
|
|
// 空头状态
|
|
if(!Enable_Short_Martin) {
|
|
short_status += "已禁用";
|
|
} else if(!is_short_martin_active) {
|
|
short_status += "未激活";
|
|
// 检查阻止重启的原因
|
|
if(Enable_ADX_Filter && adx_trend_filter_active && is_uptrend) {
|
|
short_status += "(被ADX阻止)";
|
|
} else if(Enable_DEMA_Risk_Control && short_dema_pause) {
|
|
short_status += "(被DEMA阻止)";
|
|
}
|
|
} else {
|
|
short_status += "活跃";
|
|
// 检查加仓阻止原因
|
|
if(Enable_ADX_Filter && adx_trend_filter_active && is_uptrend) {
|
|
short_status += "(加仓被ADX阻止)";
|
|
} else if(Enable_DEMA_Risk_Control && short_dema_pause) {
|
|
short_status += "(加仓被DEMA阻止)";
|
|
} else if(Enable_Risk_Control && short_add_count_this_bar >= Max_Add_Per_Bar) {
|
|
short_status += "(加仓次数超限)";
|
|
} else if(Enable_Risk_Control && short_pause_after_volatile) {
|
|
short_status += "(剧烈波动暂停)";
|
|
}
|
|
}
|
|
|
|
Print(long_status, " | ", short_status);
|
|
last_status_output = TimeCurrent();
|
|
}
|
|
|
|
// 检查价格变动并触发加仓
|
|
CheckPriceMovement(current_price);
|
|
|
|
// 检查出场条件
|
|
if(is_long_martin_active && Enable_Long_Martin) {
|
|
CheckLongExitConditions();
|
|
}
|
|
|
|
if(is_short_martin_active && Enable_Short_Martin) {
|
|
CheckShortExitConditions();
|
|
}
|
|
|
|
// 检查是否需要重新启动马丁批次
|
|
CheckRestartMartin();
|
|
|
|
// 若使用异步平仓,轮询平仓完成与超时
|
|
if(Use_Async_Close) {
|
|
// 多头异步收尾
|
|
if(long_async_closing) {
|
|
int remaining_long = GetLongPositionCount();
|
|
if(remaining_long == 0) {
|
|
long_async_closing = false;
|
|
// 重置多头马丁状态
|
|
is_long_martin_active = false;
|
|
long_last_entry_price = 0.0;
|
|
long_current_lot_size = Initial_Lots;
|
|
long_trailing_stop_level = 0.0;
|
|
long_highest_profit = 0.0;
|
|
long_martin_add_count = 0;
|
|
// 重置DEMA风险控制状态
|
|
long_dema_risk_active = false;
|
|
long_dema_pause = false;
|
|
long_max_lots = 0.0;
|
|
// 重置大仓位模式状态
|
|
long_large_position_mode = false;
|
|
long_price_track_level = 0.0;
|
|
Print("多头异步平仓完成,状态已重置,等待重新启动");
|
|
} else {
|
|
// 超时检查
|
|
if(TimeCurrent() - long_async_start_time > Async_Close_Timeout_Seconds) {
|
|
Print("多头异步平仓超时,切换为同步强制平仓...");
|
|
ForceSyncCloseLong();
|
|
}
|
|
// 重试检查
|
|
else if(TimeCurrent() - long_async_last_retry_time >= Async_Close_Retry_Seconds && long_async_retries < Async_Close_Max_Retries) {
|
|
long_async_last_retry_time = TimeCurrent();
|
|
long_async_retries++;
|
|
Print("多头异步平仓重试 第", long_async_retries, "次,剩余订单=", remaining_long);
|
|
SubmitAsyncCloseLong();
|
|
}
|
|
}
|
|
}
|
|
|
|
// 空头异步收尾
|
|
if(short_async_closing) {
|
|
int remaining_short = GetShortPositionCount();
|
|
if(remaining_short == 0) {
|
|
short_async_closing = false;
|
|
// 重置空头马丁状态
|
|
is_short_martin_active = false;
|
|
short_last_entry_price = 0.0;
|
|
short_current_lot_size = Initial_Lots;
|
|
short_trailing_stop_level = 0.0;
|
|
short_highest_profit = 0.0;
|
|
short_martin_add_count = 0;
|
|
// 重置DEMA风险控制状态
|
|
short_dema_risk_active = false;
|
|
short_dema_pause = false;
|
|
short_max_lots = 0.0;
|
|
// 重置大仓位模式状态
|
|
short_large_position_mode = false;
|
|
short_price_track_level = 0.0;
|
|
Print("空头异步平仓完成,状态已重置,等待重新启动");
|
|
} else {
|
|
if(TimeCurrent() - short_async_start_time > Async_Close_Timeout_Seconds) {
|
|
Print("空头异步平仓超时,切换为同步强制平仓...");
|
|
ForceSyncCloseShort();
|
|
} else if(TimeCurrent() - short_async_last_retry_time >= Async_Close_Retry_Seconds && short_async_retries < Async_Close_Max_Retries) {
|
|
short_async_last_retry_time = TimeCurrent();
|
|
short_async_retries++;
|
|
Print("空头异步平仓重试 第", short_async_retries, "次,剩余订单=", remaining_short);
|
|
SubmitAsyncCloseShort();
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
//+------------------------------------------------------------------+
|
|
//| 检查是否在允许交易的时间 |
|
|
//+------------------------------------------------------------------+
|
|
bool IsInTradingTime() {
|
|
if(!Enable_Time_Filter)
|
|
return true;
|
|
|
|
MqlDateTime dt;
|
|
TimeToStruct(TimeCurrent(), dt);
|
|
int current_hour = dt.hour;
|
|
|
|
// 检查是否在交易时间段内
|
|
if(Trade_Start_Hour <= Trade_End_Hour) {
|
|
// 交易时间在同一天内(如6:00-22:00)
|
|
return (current_hour >= Trade_Start_Hour && current_hour < Trade_End_Hour);
|
|
} else {
|
|
// 交易时间跨越午夜(如22:00-次日6:00)
|
|
return (current_hour >= Trade_Start_Hour || current_hour < Trade_End_Hour);
|
|
}
|
|
}
|
|
|
|
//+------------------------------------------------------------------+
|
|
//| 启动双向马丁 |
|
|
//+------------------------------------------------------------------+
|
|
void StartDualMartin() {
|
|
double current_price = SymbolInfoDouble(_Symbol, SYMBOL_BID);
|
|
|
|
// 根据参数决定是否启动多头马丁
|
|
if(Enable_Long_Martin) {
|
|
if(OpenPosition(ORDER_TYPE_BUY, Initial_Lots)) {
|
|
is_long_martin_active = true;
|
|
long_last_entry_price = current_price;
|
|
long_current_lot_size = Initial_Lots;
|
|
long_trailing_stop_level = 0.0;
|
|
long_highest_profit = 0.0;
|
|
long_martin_add_count = 0;
|
|
Print("启动多头马丁,入场价格: ", current_price, ",手数: ", Initial_Lots);
|
|
}
|
|
} else {
|
|
Print("多头马丁已禁用,仅启动空头马丁");
|
|
}
|
|
|
|
// 根据参数决定是否启动空头马丁
|
|
if(Enable_Short_Martin) {
|
|
if(OpenPosition(ORDER_TYPE_SELL, Initial_Lots)) {
|
|
is_short_martin_active = true;
|
|
short_last_entry_price = current_price;
|
|
short_current_lot_size = Initial_Lots;
|
|
short_trailing_stop_level = 0.0;
|
|
short_highest_profit = 0.0;
|
|
short_martin_add_count = 0;
|
|
Print("启动空头马丁,入场价格: ", current_price, ",手数: ", Initial_Lots);
|
|
}
|
|
} else {
|
|
Print("空头马丁已禁用,仅启动多头马丁");
|
|
}
|
|
|
|
last_check_price = current_price;
|
|
|
|
// 根据启用的马丁方向显示启动信息
|
|
if(Enable_Long_Martin && Enable_Short_Martin) {
|
|
Print("双向马丁启动完成,当前价格: ", current_price);
|
|
} else if(Enable_Long_Martin) {
|
|
Print("多头马丁启动完成,当前价格: ", current_price);
|
|
} else if(Enable_Short_Martin) {
|
|
Print("空头马丁启动完成,当前价格: ", current_price);
|
|
} else {
|
|
Print("警告:多空马丁都已禁用,EA将不会进行任何交易!");
|
|
}
|
|
}
|
|
|
|
//+------------------------------------------------------------------+
|
|
//| 检查价格变动并触发加仓 |
|
|
//+------------------------------------------------------------------+
|
|
void CheckPriceMovement(double current_price) {
|
|
if(last_check_price == 0.0) {
|
|
last_check_price = current_price;
|
|
return;
|
|
}
|
|
|
|
// 计算当前触发距离(仅用于预览,实际比较时重新计算)
|
|
double preview_distance = CalculateAddOnDistance(1);
|
|
|
|
// 多头加仓:根据ATR或固定阈值动态确定加仓距离
|
|
if(is_long_martin_active && Enable_Long_Martin) {
|
|
// 风险控制:检查单根K线加仓次数限制
|
|
if(Enable_Risk_Control && long_add_count_this_bar >= Max_Add_Per_Bar) {
|
|
static datetime last_bar_limit_log = 0;
|
|
if(TimeCurrent() - last_bar_limit_log >= 60) { // 每分钟记录一次
|
|
Print("风险控制:多头马丁单根K线加仓次数已达上限(", Max_Add_Per_Bar, "次),暂停加仓");
|
|
last_bar_limit_log = TimeCurrent();
|
|
}
|
|
return;
|
|
}
|
|
|
|
// 风险控制:检查剧烈波动暂停
|
|
if(Enable_Risk_Control && long_pause_after_volatile) {
|
|
static datetime last_volatile_pause_log = 0;
|
|
if(TimeCurrent() - last_volatile_pause_log >= 60) { // 每分钟记录一次
|
|
Print("风险控制:多头马丁因剧烈下降K线暂停加仓,剩余", long_pause_bars_remaining, "根K线");
|
|
last_volatile_pause_log = TimeCurrent();
|
|
}
|
|
return;
|
|
}
|
|
|
|
// DEMA风险控制:检查多头是否因DEMA风险控制暂停
|
|
if(Enable_DEMA_Risk_Control && long_dema_pause) {
|
|
if(Enable_DEMA_Status_Log) {
|
|
static datetime last_dema_pause_log = 0;
|
|
if(TimeCurrent() - last_dema_pause_log >= 300) { // 每5分钟记录一次,减少日志
|
|
Print("DEMA风险控制:多头马丁因DEMA信号暂停加仓,最大手数=", DoubleToString(long_max_lots, 2), ",开仓时间=", TimeToString(long_last_entry_price > 0 ? TimeCurrent() : 0));
|
|
last_dema_pause_log = TimeCurrent();
|
|
}
|
|
}
|
|
return;
|
|
}
|
|
|
|
// ADX趋势过滤:下降趋势时停止多头加仓
|
|
if(adx_trend_filter_active && is_downtrend) {
|
|
// 不进行多头加仓,但记录日志
|
|
static datetime last_downtrend_block_log = 0;
|
|
if(TimeCurrent() - last_downtrend_block_log >= 60) { // 每分钟记录一次
|
|
Print("ADX趋势过滤:下降趋势中,阻止多头马丁加仓");
|
|
last_downtrend_block_log = TimeCurrent();
|
|
}
|
|
return;
|
|
}
|
|
|
|
double long_price_change = long_last_entry_price - current_price;
|
|
int next_long_index = long_martin_add_count + 1; // 第几次加仓(从1开始)
|
|
double required_distance_long = CalculateAddOnDistance(next_long_index);
|
|
if(long_price_change > required_distance_long) {
|
|
double new_lot_size = NormalizeDouble(long_current_lot_size * Martingale_Multiplier, 2);
|
|
if(OpenPosition(ORDER_TYPE_BUY, new_lot_size)) {
|
|
long_martin_add_count++;
|
|
long_add_count_this_bar++; // 风险控制:增加当前K线加仓计数
|
|
long_last_entry_price = current_price;
|
|
long_current_lot_size = new_lot_size;
|
|
|
|
// 如果处于大仓位模式,重新计算盈亏平衡价位
|
|
if(long_large_position_mode) {
|
|
long_price_track_level = GetAvgPrice(POSITION_TYPE_BUY);
|
|
Print("多头大仓位模式加仓后更新盈亏平衡价位: ", long_price_track_level);
|
|
}
|
|
|
|
if(last_distance_used_atr) {
|
|
Print("多头加仓成功(ATR),第", long_martin_add_count, "次,加仓手数:", new_lot_size,
|
|
",价格:", current_price, ",价差:+", long_price_change,
|
|
",触发: ATR=", last_distance_atr, " * ", ATR_Distance_Multiplier,
|
|
" = ", required_distance_long, "; 阈值=", ATR_Threshold);
|
|
} else {
|
|
Print("多头加仓成功(固定阈值),第", long_martin_add_count, "次,加仓手数:", new_lot_size,
|
|
",价格:", current_price, ",价差:+", long_price_change,
|
|
",触发: 固定阈值=", required_distance_long);
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
// 空头加仓:根据ATR或固定阈值动态确定加仓距离
|
|
if(is_short_martin_active && Enable_Short_Martin) {
|
|
// 风险控制:检查单根K线加仓次数限制
|
|
if(Enable_Risk_Control && short_add_count_this_bar >= Max_Add_Per_Bar) {
|
|
static datetime last_bar_limit_log = 0;
|
|
if(TimeCurrent() - last_bar_limit_log >= 60) { // 每分钟记录一次
|
|
Print("风险控制:空头马丁单根K线加仓次数已达上限(", Max_Add_Per_Bar, "次),暂停加仓");
|
|
last_bar_limit_log = TimeCurrent();
|
|
}
|
|
return;
|
|
}
|
|
|
|
// 风险控制:检查剧烈波动暂停
|
|
if(Enable_Risk_Control && short_pause_after_volatile) {
|
|
static datetime last_volatile_pause_log = 0;
|
|
if(TimeCurrent() - last_volatile_pause_log >= 60) { // 每分钟记录一次
|
|
Print("风险控制:空头马丁因剧烈上升K线暂停加仓,剩余", short_pause_bars_remaining, "根K线");
|
|
last_volatile_pause_log = TimeCurrent();
|
|
}
|
|
return;
|
|
}
|
|
|
|
// DEMA风险控制:检查空头是否因DEMA风险控制暂停
|
|
if(Enable_DEMA_Risk_Control && short_dema_pause) {
|
|
if(Enable_DEMA_Status_Log) {
|
|
static datetime last_dema_pause_log = 0;
|
|
if(TimeCurrent() - last_dema_pause_log >= 300) { // 每5分钟记录一次,减少日志
|
|
Print("DEMA风险控制:空头马丁因DEMA信号暂停加仓,最大手数=", DoubleToString(short_max_lots, 2), ",开仓时间=", TimeToString(short_last_entry_price > 0 ? TimeCurrent() : 0));
|
|
last_dema_pause_log = TimeCurrent();
|
|
}
|
|
}
|
|
return;
|
|
}
|
|
|
|
// ADX趋势过滤:上升趋势时停止空头加仓
|
|
if(adx_trend_filter_active && is_uptrend) {
|
|
// 不进行空头加仓,但记录日志
|
|
static datetime last_uptrend_block_log = 0;
|
|
if(TimeCurrent() - last_uptrend_block_log >= 60) { // 每分钟记录一次
|
|
Print("ADX趋势过滤:上升趋势中,阻止空头马丁加仓");
|
|
last_uptrend_block_log = TimeCurrent();
|
|
}
|
|
return;
|
|
}
|
|
|
|
double short_price_change = current_price - short_last_entry_price;
|
|
int next_short_index = short_martin_add_count + 1; // 第几次加仓(从1开始)
|
|
double required_distance_short = CalculateAddOnDistance(next_short_index);
|
|
if(short_price_change > required_distance_short) {
|
|
double new_lot_size = NormalizeDouble(short_current_lot_size * Martingale_Multiplier, 2);
|
|
if(OpenPosition(ORDER_TYPE_SELL, new_lot_size)) {
|
|
short_martin_add_count++;
|
|
short_add_count_this_bar++; // 风险控制:增加当前K线加仓计数
|
|
short_last_entry_price = current_price;
|
|
short_current_lot_size = new_lot_size;
|
|
|
|
// 如果处于大仓位模式,重新计算盈亏平衡价位
|
|
if(short_large_position_mode) {
|
|
short_price_track_level = GetAvgPrice(POSITION_TYPE_SELL);
|
|
Print("空头大仓位模式加仓后更新盈亏平衡价位: ", short_price_track_level);
|
|
}
|
|
|
|
if(last_distance_used_atr) {
|
|
Print("空头加仓成功(ATR),第", short_martin_add_count, "次,加仓手数:", new_lot_size,
|
|
",价格:", current_price, ",价差:+", short_price_change,
|
|
",触发: ATR=", last_distance_atr, " * ", ATR_Distance_Multiplier,
|
|
" = ", required_distance_short, "; 阈值=", ATR_Threshold);
|
|
} else {
|
|
Print("空头加仓成功(固定阈值),第", short_martin_add_count, "次,加仓手数:", new_lot_size,
|
|
",价格:", current_price, ",价差:+", short_price_change,
|
|
",触发: 固定阈值=", required_distance_short);
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
last_check_price = current_price;
|
|
}
|
|
|
|
//+------------------------------------------------------------------+
|
|
//| 计算加仓触发距离(ATR>阈值时采用ATR*系数,否则用固定阈值) |
|
|
//+------------------------------------------------------------------+
|
|
double CalculateAddOnDistance(int next_add_index) {
|
|
// 优先使用ATR逻辑(当ATR句柄有效且ATR>阈值)
|
|
if(atr_handle != INVALID_HANDLE) {
|
|
double atr_values[1];
|
|
int copied = CopyBuffer(atr_handle, 0, 0, 1, atr_values);
|
|
if(copied == 1) {
|
|
double atr_now = atr_values[0];
|
|
if(atr_now > ATR_Threshold) {
|
|
double distance_atr = atr_now * ATR_Distance_Multiplier;
|
|
last_distance_used_atr = true;
|
|
last_distance_atr = atr_now;
|
|
last_distance_final = NormalizeDouble(distance_atr, 2);
|
|
return last_distance_final;
|
|
}
|
|
}
|
|
}
|
|
|
|
// 否则使用固定价格阈值
|
|
last_distance_used_atr = false;
|
|
last_distance_atr = 0.0;
|
|
last_distance_final = NormalizeDouble(Price_Threshold, 2);
|
|
return last_distance_final;
|
|
}
|
|
|
|
//+------------------------------------------------------------------+
|
|
//| 检查ADX趋势状态 |
|
|
//+------------------------------------------------------------------+
|
|
void CheckADXTrend() {
|
|
if(!Enable_ADX_Filter || adx_handle == INVALID_HANDLE) {
|
|
adx_trend_filter_active = false;
|
|
is_uptrend = false;
|
|
is_downtrend = false;
|
|
return;
|
|
}
|
|
|
|
double adx_values[1];
|
|
double plus_di_values[1];
|
|
double minus_di_values[1];
|
|
|
|
// 复制ADX、+DI、-DI数据
|
|
int copied_adx = CopyBuffer(adx_handle, 0, 0, 1, adx_values); // ADX主值
|
|
int copied_plus_di = CopyBuffer(adx_handle, 1, 0, 1, plus_di_values); // +DI
|
|
int copied_minus_di = CopyBuffer(adx_handle, 2, 0, 1, minus_di_values); // -DI
|
|
|
|
if(copied_adx == 1 && copied_plus_di == 1 && copied_minus_di == 1) {
|
|
double adx_now = adx_values[0];
|
|
double plus_di_now = plus_di_values[0];
|
|
double minus_di_now = minus_di_values[0];
|
|
|
|
bool was_filter_active = adx_trend_filter_active;
|
|
bool was_uptrend = is_uptrend;
|
|
bool was_downtrend = is_downtrend;
|
|
|
|
// 当ADX大于阈值时,激活趋势过滤
|
|
if(adx_now > ADX_Threshold) {
|
|
adx_trend_filter_active = true;
|
|
|
|
// 比较+DI和-DI确定趋势方向
|
|
if(plus_di_now > minus_di_now) {
|
|
// +DI > -DI,上升趋势,停止空头马丁加仓
|
|
is_uptrend = true;
|
|
is_downtrend = false;
|
|
|
|
if(!was_uptrend || !was_filter_active) {
|
|
Print("ADX趋势过滤激活:上升趋势,ADX=", DoubleToString(adx_now, 2),
|
|
",+DI=", DoubleToString(plus_di_now, 2), " > -DI=", DoubleToString(minus_di_now, 2),
|
|
",停止空头马丁加仓");
|
|
}
|
|
} else if(plus_di_now < minus_di_now) {
|
|
// +DI < -DI,下降趋势,停止多头马丁加仓
|
|
is_uptrend = false;
|
|
is_downtrend = true;
|
|
|
|
if(!was_downtrend || !was_filter_active) {
|
|
Print("ADX趋势过滤激活:下降趋势,ADX=", DoubleToString(adx_now, 2),
|
|
",+DI=", DoubleToString(plus_di_now, 2), " < -DI=", DoubleToString(minus_di_now, 2),
|
|
",停止多头马丁加仓");
|
|
}
|
|
} else {
|
|
// +DI = -DI,趋势不明确
|
|
is_uptrend = false;
|
|
is_downtrend = false;
|
|
|
|
if(was_filter_active && (was_uptrend || was_downtrend)) {
|
|
Print("ADX趋势过滤:趋势不明确,+DI=", DoubleToString(plus_di_now, 2),
|
|
" = -DI=", DoubleToString(minus_di_now, 2), ",允许双向加仓");
|
|
}
|
|
}
|
|
} else {
|
|
// ADX小于阈值,关闭趋势过滤,允许双向加仓
|
|
adx_trend_filter_active = false;
|
|
is_uptrend = false;
|
|
is_downtrend = false;
|
|
|
|
if(was_filter_active) {
|
|
Print("ADX趋势过滤关闭:ADX=", DoubleToString(adx_now, 2), " < ", ADX_Threshold, ",允许双向加仓");
|
|
}
|
|
}
|
|
} else {
|
|
Print("ADX数据获取失败,错误:ADX=", copied_adx, ", +DI=", copied_plus_di, ", -DI=", copied_minus_di);
|
|
}
|
|
}
|
|
|
|
//+------------------------------------------------------------------+
|
|
//| 风险控制检查 |
|
|
//+------------------------------------------------------------------+
|
|
void CheckRiskControl() {
|
|
if(!Enable_Risk_Control) return;
|
|
|
|
int new_bar_index = Bars(_Symbol, _Period);
|
|
|
|
// 新K线时重置加仓计数
|
|
if(new_bar_index != current_bar_index) {
|
|
long_add_count_this_bar = 0;
|
|
short_add_count_this_bar = 0;
|
|
current_bar_index = new_bar_index;
|
|
|
|
// 检查上一根K线是否剧烈波动
|
|
if(new_bar_index > 0) {
|
|
double prev_open = iOpen(_Symbol, _Period, 1);
|
|
double prev_close = iClose(_Symbol, _Period, 1);
|
|
double bar_range = MathAbs(prev_close - prev_open);
|
|
|
|
if(bar_range > Volatile_Bar_Threshold) {
|
|
if(prev_close > prev_open) { // 剧烈上升
|
|
short_pause_after_volatile = true;
|
|
short_pause_bars_remaining = Volatile_Bar_Pause;
|
|
Print("风险控制:检测到剧烈上升K线(范围=", bar_range, "美元),暂停空头马丁", Volatile_Bar_Pause, "根K线");
|
|
} else { // 剧烈下降
|
|
long_pause_after_volatile = true;
|
|
long_pause_bars_remaining = Volatile_Bar_Pause;
|
|
Print("风险控制:检测到剧烈下降K线(范围=", bar_range, "美元),暂停多头马丁", Volatile_Bar_Pause, "根K线");
|
|
}
|
|
}
|
|
}
|
|
|
|
// 更新暂停状态
|
|
if(long_pause_after_volatile && long_pause_bars_remaining > 0) {
|
|
long_pause_bars_remaining--;
|
|
if(long_pause_bars_remaining == 0) {
|
|
long_pause_after_volatile = false;
|
|
Print("风险控制:多头马丁暂停期结束,恢复加仓");
|
|
}
|
|
}
|
|
|
|
if(short_pause_after_volatile && short_pause_bars_remaining > 0) {
|
|
short_pause_bars_remaining--;
|
|
if(short_pause_bars_remaining == 0) {
|
|
short_pause_after_volatile = false;
|
|
Print("风险控制:空头马丁暂停期结束,恢复加仓");
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
//+------------------------------------------------------------------+
|
|
//| 计算指定方向持仓的真实保本价 (含点差成本) |
|
|
//| posType: POSITION_TYPE_BUY 或 POSITION_TYPE_SELL |
|
|
//+------------------------------------------------------------------+
|
|
double GetAvgPrice(int posType) {
|
|
double totalLots = 0;
|
|
double totalCost = 0;
|
|
|
|
// 遍历持仓,计算加权开仓价
|
|
for(int i=0; i<PositionsTotal(); i++) {
|
|
ulong ticket = PositionGetTicket(i);
|
|
if(PositionSelectByTicket(ticket)) {
|
|
if(PositionGetString(POSITION_SYMBOL) == _Symbol &&
|
|
PositionGetInteger(POSITION_MAGIC) == Magic_Number &&
|
|
PositionGetInteger(POSITION_TYPE) == posType) {
|
|
double lots = PositionGetDouble(POSITION_VOLUME);
|
|
double price = PositionGetDouble(POSITION_PRICE_OPEN);
|
|
totalLots += lots;
|
|
totalCost += lots * price;
|
|
}
|
|
}
|
|
}
|
|
|
|
if(totalLots == 0) return 0;
|
|
|
|
// 加权平均开仓价
|
|
double avgPrice = totalCost / totalLots;
|
|
Print("加权平均开仓价为:", avgPrice);
|
|
|
|
// 合约大小 (黄金一般是100)
|
|
double contract_size = SymbolInfoDouble(_Symbol, SYMBOL_TRADE_CONTRACT_SIZE);
|
|
|
|
// 当前点差
|
|
double bid = SymbolInfoDouble(_Symbol, SYMBOL_BID);
|
|
double ask = SymbolInfoDouble(_Symbol, SYMBOL_ASK);
|
|
double spread = ask - bid;
|
|
|
|
// 点差对应的总成本(以美元计)
|
|
double spread_cost_total = spread * contract_size * totalLots;
|
|
|
|
// 每手分摊的价格修正
|
|
double spread_adjust = spread_cost_total / (contract_size * totalLots);
|
|
|
|
// 根据方向修正保本价
|
|
if(posType == POSITION_TYPE_BUY) {
|
|
double buyAvgPrice =avgPrice + spread_adjust;
|
|
return buyAvgPrice; // 多单需要更高的价格解套
|
|
|
|
} else {
|
|
double sellAvgPrice =avgPrice - spread_adjust;
|
|
return sellAvgPrice; // 空单需要更低的价格解套
|
|
|
|
}
|
|
}
|
|
|
|
|
|
//+------------------------------------------------------------------+
|
|
//| 检查多头出场条件 |
|
|
//+------------------------------------------------------------------+
|
|
void CheckLongExitConditions() {
|
|
double long_total_profit = GetLongTotalProfit();
|
|
double long_total_lots = GetLongTotalLots();
|
|
|
|
// 添加调试信息
|
|
static datetime last_debug_time = 0;
|
|
if(TimeCurrent() - last_debug_time >= 90) { // 每30秒打印一次调试信息
|
|
Print("[调试] 多头状态: 盈利=", long_total_profit, "美元, 总仓位=", long_total_lots, "手, 大仓位模式=",
|
|
(long_large_position_mode ? "是" : "否"), ", 盈利目标=", Profit_Target, "美元, 大仓位阈值=", Large_Position_Threshold, "手");
|
|
last_debug_time = TimeCurrent();
|
|
}
|
|
|
|
// 检查是否进入大仓位模式
|
|
if(!long_large_position_mode && long_total_lots > Large_Position_Threshold) {
|
|
long_large_position_mode = true;
|
|
// 计算马丁盈亏平衡价位(加权平均价)
|
|
long_price_track_level = GetAvgPrice(POSITION_TYPE_BUY);
|
|
Print("多头马丁进入大仓位模式,总仓位: ", long_total_lots, "手,马丁盈亏平衡价位: ", long_price_track_level);
|
|
}
|
|
|
|
// 更新最高盈利记录
|
|
if(long_total_profit > long_highest_profit) {
|
|
long_highest_profit = long_total_profit;
|
|
}
|
|
|
|
if(long_large_position_mode) {
|
|
// 大仓位模式:基于盈亏平衡价位的价格追踪止盈
|
|
double current_price = SymbolInfoDouble(_Symbol, SYMBOL_BID);
|
|
double target_exit_price = long_price_track_level + Price_Track_Distance;
|
|
|
|
Print("[调试] 多头大仓位: 当前价格=", current_price, ", 盈亏平衡价位=", long_price_track_level, ", 目标出场价位=", target_exit_price, ", 总盈亏=", long_total_profit, "美元");
|
|
|
|
if(current_price >= target_exit_price) {
|
|
Print("多头大仓位价格追踪触发,平仓所有多头订单,盈亏平衡价位: ", long_price_track_level,
|
|
",当前价格: ", current_price, ",目标出场价位: ", target_exit_price,
|
|
",最终盈利: $", long_total_profit);
|
|
CloseLongPositions();
|
|
}
|
|
} else {
|
|
// 小仓位模式:盈利保护机制
|
|
if(long_total_profit >= Profit_Target) {
|
|
double protection_level = long_total_profit * Profit_Protection_Ratio;
|
|
|
|
if(long_trailing_stop_level == 0.0) {
|
|
// 首次激活盈利保护
|
|
long_trailing_stop_level = protection_level;
|
|
Print("多头小仓位盈利保护激活,保护水平: ", protection_level, "美元,当前盈利: ", long_total_profit, "美元");
|
|
} else {
|
|
// 动态更新保护水平:只有当新的保护水平更高时才更新
|
|
if(protection_level > long_trailing_stop_level) {
|
|
double old_level = long_trailing_stop_level;
|
|
long_trailing_stop_level = protection_level;
|
|
Print("多头小仓位盈利保护更新:从", old_level, "美元 -> ", long_trailing_stop_level, "美元,当前盈利: ", long_total_profit, "美元");
|
|
}
|
|
}
|
|
|
|
// 检查是否触发保护
|
|
if(long_total_profit <= long_trailing_stop_level) {
|
|
Print("多头小仓位盈利保护触发,平仓所有多头订单,最终盈利: $", long_total_profit);
|
|
CloseLongPositions();
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
//+------------------------------------------------------------------+
|
|
//| 检查空头出场条件 |
|
|
//+------------------------------------------------------------------+
|
|
void CheckShortExitConditions() {
|
|
double short_total_profit = GetShortTotalProfit();
|
|
double short_total_lots = GetShortTotalLots();
|
|
|
|
// 添加调试信息
|
|
static datetime last_debug_time_short = 0;
|
|
if(TimeCurrent() - last_debug_time_short >= 90) { // 每30秒打印一次调试信息
|
|
Print("[调试] 空头状态: 盈利=", short_total_profit, "美元, 总仓位=", short_total_lots, "手, 大仓位模式=",
|
|
(short_large_position_mode ? "是" : "否"), ", 盈利目标=", Profit_Target, "美元, 大仓位阈值=", Large_Position_Threshold, "手");
|
|
last_debug_time_short = TimeCurrent();
|
|
}
|
|
|
|
// 检查是否进入大仓位模式
|
|
if(!short_large_position_mode && short_total_lots > Large_Position_Threshold) {
|
|
short_large_position_mode = true;
|
|
// 计算马丁盈亏平衡价位(加权平均价)
|
|
short_price_track_level = GetAvgPrice(POSITION_TYPE_SELL);
|
|
Print("空头马丁进入大仓位模式,总仓位: ", short_total_lots, "手,马丁盈亏平衡价位: ", short_price_track_level);
|
|
}
|
|
|
|
// 更新最高盈利记录
|
|
if(short_total_profit > short_highest_profit) {
|
|
short_highest_profit = short_total_profit;
|
|
}
|
|
|
|
if(short_large_position_mode) {
|
|
// 大仓位模式:基于盈亏平衡价位的价格追踪止盈
|
|
double current_price = SymbolInfoDouble(_Symbol, SYMBOL_BID);
|
|
double target_exit_price = short_price_track_level - Price_Track_Distance;
|
|
|
|
Print("[调试] 空头大仓位: 当前价格=", current_price, ", 盈亏平衡价位=", short_price_track_level, ", 目标出场价位=", target_exit_price, ", 总盈亏=", short_total_profit, "美元");
|
|
|
|
if(current_price <= target_exit_price) {
|
|
Print("空头大仓位价格追踪触发,平仓所有空头订单,盈亏平衡价位: ", short_price_track_level,
|
|
",当前价格: ", current_price, ",目标出场价位: ", target_exit_price,
|
|
",最终盈利: $", short_total_profit);
|
|
CloseShortPositions();
|
|
}
|
|
} else {
|
|
// 小仓位模式:盈利保护机制
|
|
if(short_total_profit >= Profit_Target) {
|
|
double protection_level = short_total_profit * Profit_Protection_Ratio;
|
|
|
|
if(short_trailing_stop_level == 0.0) {
|
|
// 首次激活盈利保护
|
|
short_trailing_stop_level = protection_level;
|
|
Print("空头小仓位盈利保护激活,保护水平: ", protection_level, "美元,当前盈利: ", short_total_profit, "美元");
|
|
} else {
|
|
// 动态更新保护水平:只有当新的保护水平更高时才更新
|
|
if(protection_level > short_trailing_stop_level) {
|
|
double old_level = short_trailing_stop_level;
|
|
short_trailing_stop_level = protection_level;
|
|
Print("空头小仓位盈利保护更新:从", old_level, "美元 -> ", short_trailing_stop_level, "美元,当前盈利: ", short_total_profit, "美元");
|
|
}
|
|
}
|
|
|
|
// 检查是否触发保护
|
|
if(short_total_profit <= short_trailing_stop_level) {
|
|
Print("空头小仓位盈利保护触发,平仓所有空头订单,最终盈利: $", short_total_profit);
|
|
CloseShortPositions();
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
//+------------------------------------------------------------------+
|
|
//| 检查是否需要重新启动马丁批次 |
|
|
//+------------------------------------------------------------------+
|
|
void CheckRestartMartin() {
|
|
// 检查多头马丁重新启动:根据参数决定是否重新启动多头马丁
|
|
if(Enable_Long_Martin && !is_long_martin_active && GetLongPositionCount() == 0) {
|
|
// ADX趋势过滤:下降趋势时阻止多头马丁重启
|
|
if(Enable_ADX_Filter && adx_trend_filter_active && is_downtrend) {
|
|
static datetime last_downtrend_restart_block_log = 0;
|
|
if(TimeCurrent() - last_downtrend_restart_block_log >= 60) { // 每分钟记录一次
|
|
Print("ADX趋势过滤:下降趋势中,阻止多头马丁重启");
|
|
last_downtrend_restart_block_log = TimeCurrent();
|
|
}
|
|
return;
|
|
}
|
|
|
|
// DEMA风险控制:检查多头是否因DEMA风险控制暂停重启
|
|
if(Enable_DEMA_Risk_Control && long_dema_pause) {
|
|
if(Enable_DEMA_Status_Log) {
|
|
static datetime last_dema_restart_block_log = 0;
|
|
if(TimeCurrent() - last_dema_restart_block_log >= 300) { // 每5分钟记录一次,减少日志
|
|
Print("DEMA风险控制:多头马丁因DEMA信号暂停重启,最大手数=", DoubleToString(long_max_lots, 2), ",开仓时间=", TimeToString(long_last_entry_price > 0 ? TimeCurrent() : 0));
|
|
last_dema_restart_block_log = TimeCurrent();
|
|
}
|
|
}
|
|
return;
|
|
}
|
|
|
|
// 添加延迟检查,确保上一批次完全平仓后再启动
|
|
static datetime long_last_close_time = 0;
|
|
|
|
// 如果时间标记为0,说明是第一次检查,立即设置当前时间
|
|
if(long_last_close_time == 0) {
|
|
long_last_close_time = TimeCurrent();
|
|
Print("多头马丁等待重启,开始计时...");
|
|
}
|
|
|
|
// 等待指定时间确保完全平仓
|
|
if(TimeCurrent() - long_last_close_time >= Restart_Delay_Seconds) {
|
|
double current_price = SymbolInfoDouble(_Symbol, SYMBOL_BID);
|
|
if(OpenPosition(ORDER_TYPE_BUY, Initial_Lots)) {
|
|
is_long_martin_active = true;
|
|
long_last_entry_price = current_price;
|
|
long_current_lot_size = Initial_Lots;
|
|
long_trailing_stop_level = 0.0;
|
|
long_highest_profit = 0.0;
|
|
long_martin_add_count = 0;
|
|
// 重置大仓位模式状态
|
|
long_large_position_mode = false;
|
|
long_price_track_level = 0.0;
|
|
long_last_close_time = 0; // 重置时间
|
|
Print("多头马丁重新启动,入场价格: ", current_price, ",手数: ", Initial_Lots);
|
|
}
|
|
} else {
|
|
// 显示等待时间
|
|
int remaining_time = Restart_Delay_Seconds - (int)(TimeCurrent() - long_last_close_time);
|
|
if(remaining_time > 0) {
|
|
Print("多头马丁等待重启,剩余时间: ", remaining_time, "秒");
|
|
}
|
|
}
|
|
}
|
|
|
|
// 检查空头马丁重新启动:根据参数决定是否重新启动空头马丁
|
|
if(Enable_Short_Martin && !is_short_martin_active && GetShortPositionCount() == 0) {
|
|
// ADX趋势过滤:上升趋势时阻止空头马丁重启
|
|
if(Enable_ADX_Filter && adx_trend_filter_active && is_uptrend) {
|
|
static datetime last_uptrend_restart_block_log = 0;
|
|
if(TimeCurrent() - last_uptrend_restart_block_log >= 60) { // 每分钟记录一次
|
|
Print("ADX趋势过滤:上升趋势中,阻止空头马丁重启");
|
|
last_uptrend_restart_block_log = TimeCurrent();
|
|
}
|
|
return;
|
|
}
|
|
|
|
// DEMA风险控制:检查空头马丁因DEMA信号暂停重启
|
|
if(Enable_DEMA_Risk_Control && short_dema_pause) {
|
|
if(Enable_DEMA_Status_Log) {
|
|
static datetime last_dema_restart_block_log = 0;
|
|
if(TimeCurrent() - last_dema_restart_block_log >= 300) { // 每5分钟记录一次,减少日志
|
|
Print("DEMA风险控制:空头马丁因DEMA信号暂停重启,最大手数=", DoubleToString(short_max_lots, 2), ",开仓时间=", TimeToString(short_last_entry_price > 0 ? TimeCurrent() : 0));
|
|
last_dema_restart_block_log = TimeCurrent();
|
|
}
|
|
}
|
|
return;
|
|
}
|
|
|
|
// 添加延迟检查,确保上一批次完全平仓后再启动
|
|
static datetime short_last_close_time = 0;
|
|
|
|
// 如果时间标记为0,说明是第一次检查,立即设置当前时间
|
|
if(short_last_close_time == 0) {
|
|
short_last_close_time = TimeCurrent();
|
|
Print("空头马丁等待重启,开始计时...");
|
|
}
|
|
|
|
// 等待指定时间确保完全平仓
|
|
if(TimeCurrent() - short_last_close_time >= Restart_Delay_Seconds) {
|
|
double current_price = SymbolInfoDouble(_Symbol, SYMBOL_BID);
|
|
if(OpenPosition(ORDER_TYPE_SELL, Initial_Lots)) {
|
|
is_short_martin_active = true;
|
|
short_last_entry_price = current_price;
|
|
short_current_lot_size = Initial_Lots;
|
|
short_trailing_stop_level = 0.0;
|
|
short_highest_profit = 0.0;
|
|
short_martin_add_count = 0;
|
|
// 重置大仓位模式状态
|
|
short_large_position_mode = false;
|
|
short_price_track_level = 0.0;
|
|
short_last_close_time = 0; // 重置时间
|
|
Print("空头马丁重新启动,入场价格: ", current_price, ",手数: ", Initial_Lots);
|
|
}
|
|
} else {
|
|
// 显示等待时间
|
|
int remaining_time = Restart_Delay_Seconds - (int)(TimeCurrent() - short_last_close_time);
|
|
if(remaining_time > 0) {
|
|
Print("空头马丁等待重启,剩余时间: ", remaining_time, "秒");
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
//+------------------------------------------------------------------+
|
|
//| 开仓函数 |
|
|
//+------------------------------------------------------------------+
|
|
bool OpenPosition(ENUM_ORDER_TYPE order_type, double lot_size) {
|
|
double price = (order_type == ORDER_TYPE_BUY) ?
|
|
SymbolInfoDouble(_Symbol, SYMBOL_ASK) :
|
|
SymbolInfoDouble(_Symbol, SYMBOL_BID);
|
|
|
|
if(trade.PositionOpen(_Symbol, order_type, lot_size, price, 0, 0, EA_Comment)) {
|
|
Print("开仓成功 - 类型: ", (order_type == ORDER_TYPE_BUY ? "买入" : "卖出"),
|
|
",手数: ", lot_size, ",价格: ", price);
|
|
return true;
|
|
} else {
|
|
Print("开仓失败 - 错误代码: ", trade.ResultRetcode());
|
|
return false;
|
|
}
|
|
}
|
|
|
|
//+------------------------------------------------------------------+
|
|
//| 平仓所有多头持仓 |
|
|
//+------------------------------------------------------------------+
|
|
void CloseLongPositions() {
|
|
double final_profit = GetLongTotalProfit();
|
|
int total_positions = GetLongPositionCount();
|
|
|
|
Print("开始批量平仓多头订单,当前总盈利=", final_profit, "美元,持仓数量=", total_positions);
|
|
|
|
if(Use_Async_Close) {
|
|
// 异步提交平仓请求:遍历并逐个提交,但不等待结果
|
|
int submit_count = 0;
|
|
trade.SetAsyncMode(true);
|
|
for(int i = PositionsTotal() - 1; i >= 0; i--) {
|
|
ulong ticket = PositionGetTicket(i);
|
|
if(PositionSelectByTicket(ticket)) {
|
|
if(PositionGetString(POSITION_SYMBOL) == _Symbol &&
|
|
PositionGetInteger(POSITION_MAGIC) == Magic_Number &&
|
|
PositionGetInteger(POSITION_TYPE) == POSITION_TYPE_BUY) {
|
|
if(trade.PositionClose(ticket)) {
|
|
submit_count++;
|
|
} else {
|
|
Print("提交多头异步平仓失败: ", ticket, ", 错误: ", trade.ResultRetcode());
|
|
}
|
|
}
|
|
}
|
|
}
|
|
trade.SetAsyncMode(false);
|
|
long_async_closing = true;
|
|
long_async_start_time = TimeCurrent();
|
|
long_async_retries = 0;
|
|
long_async_last_retry_time = TimeCurrent();
|
|
Print("已异步提交多头平仓指令,提交数量=", submit_count, ",等待成交...");
|
|
return;
|
|
}
|
|
|
|
// 同步路径:沿用原批量+逐个关闭逻辑
|
|
bool close_success = false;
|
|
int positions_closed = 0;
|
|
if(BatchCloseLongPositions()) {
|
|
Print("多头批量平仓成功,关闭了", total_positions, "个订单,最终盈利=", final_profit, "美元");
|
|
close_success = true;
|
|
} else {
|
|
Print("多头批量平仓失败,尝试逐个平仓作为备选方案...");
|
|
positions_closed = 0;
|
|
for(int i = PositionsTotal() - 1; i >= 0; i--) {
|
|
ulong ticket = PositionGetTicket(i);
|
|
if(PositionSelectByTicket(ticket)) {
|
|
if(PositionGetString(POSITION_SYMBOL) == _Symbol &&
|
|
PositionGetInteger(POSITION_MAGIC) == Magic_Number &&
|
|
PositionGetInteger(POSITION_TYPE) == POSITION_TYPE_BUY) {
|
|
if(trade.PositionClose(ticket)) {
|
|
positions_closed++;
|
|
} else {
|
|
Print("平仓多头订单失败: ", ticket, ", 错误: ", trade.ResultRetcode());
|
|
}
|
|
}
|
|
}
|
|
}
|
|
Print("备选逐个平仓完成,成功关闭了", positions_closed, "个订单");
|
|
int remaining_positions = GetLongPositionCount();
|
|
if(remaining_positions == 0) {
|
|
close_success = true;
|
|
Print("验证完成:所有多头订单已成功平仓");
|
|
} else {
|
|
Print("警告:仍有", remaining_positions, "个多头订单未能平仓!");
|
|
Print("这些订单将继续保留,EA不会重置马丁状态");
|
|
return; // 不重置状态,等待下次检查
|
|
}
|
|
}
|
|
if(close_success) {
|
|
is_long_martin_active = false;
|
|
long_last_entry_price = 0.0;
|
|
long_current_lot_size = Initial_Lots;
|
|
long_trailing_stop_level = 0.0;
|
|
long_highest_profit = 0.0;
|
|
long_martin_add_count = 0;
|
|
// 重置大仓位模式状态
|
|
long_large_position_mode = false;
|
|
long_price_track_level = 0.0;
|
|
// 重置DEMA风险控制状态
|
|
long_dema_risk_active = false;
|
|
long_dema_pause = false;
|
|
long_max_lots = 0.0;
|
|
Print("多头马丁状态已重置,等待重新启动");
|
|
Print("多头马丁平仓完成,等待", Restart_Delay_Seconds, "秒后重启检查");
|
|
}
|
|
}
|
|
|
|
//+------------------------------------------------------------------+
|
|
//| 平仓所有空头持仓 |
|
|
//+------------------------------------------------------------------+
|
|
void CloseShortPositions() {
|
|
double final_profit = GetShortTotalProfit();
|
|
int total_positions = GetShortPositionCount();
|
|
|
|
Print("开始批量平仓空头订单,当前总盈利=", final_profit, "美元,持仓数量=", total_positions);
|
|
|
|
if(Use_Async_Close) {
|
|
int submit_count = 0;
|
|
trade.SetAsyncMode(true);
|
|
for(int i = PositionsTotal() - 1; i >= 0; i--) {
|
|
ulong ticket = PositionGetTicket(i);
|
|
if(PositionSelectByTicket(ticket)) {
|
|
if(PositionGetString(POSITION_SYMBOL) == _Symbol &&
|
|
PositionGetInteger(POSITION_MAGIC) == Magic_Number &&
|
|
PositionGetInteger(POSITION_TYPE) == POSITION_TYPE_SELL) {
|
|
if(trade.PositionClose(ticket)) {
|
|
submit_count++;
|
|
} else {
|
|
Print("提交空头异步平仓失败: ", ticket, ", 错误: ", trade.ResultRetcode());
|
|
}
|
|
}
|
|
}
|
|
}
|
|
trade.SetAsyncMode(false);
|
|
short_async_closing = true;
|
|
short_async_start_time = TimeCurrent();
|
|
short_async_retries = 0;
|
|
short_async_last_retry_time = TimeCurrent();
|
|
Print("已异步提交空头平仓指令,提交数量=", submit_count, ",等待成交...");
|
|
return;
|
|
}
|
|
|
|
// 同步路径:沿用原批量+逐个关闭逻辑
|
|
bool close_success = false;
|
|
int positions_closed = 0;
|
|
if(BatchCloseShortPositions()) {
|
|
Print("空头批量平仓成功,关闭了", total_positions, "个订单,最终盈利=", final_profit, "美元");
|
|
close_success = true;
|
|
} else {
|
|
Print("空头批量平仓失败,尝试逐个平仓作为备选方案...");
|
|
positions_closed = 0;
|
|
for(int i = PositionsTotal() - 1; i >= 0; i--) {
|
|
ulong ticket = PositionGetTicket(i);
|
|
if(PositionSelectByTicket(ticket)) {
|
|
if(PositionGetString(POSITION_SYMBOL) == _Symbol &&
|
|
PositionGetInteger(POSITION_MAGIC) == Magic_Number &&
|
|
PositionGetInteger(POSITION_TYPE) == POSITION_TYPE_SELL) {
|
|
if(trade.PositionClose(ticket)) {
|
|
positions_closed++;
|
|
} else {
|
|
Print("平仓空头订单失败: ", ticket, ", 错误: ", trade.ResultRetcode());
|
|
}
|
|
}
|
|
}
|
|
}
|
|
Print("备选逐个平仓完成,成功关闭了", positions_closed, "个订单");
|
|
int remaining_positions = GetShortPositionCount();
|
|
if(remaining_positions == 0) {
|
|
close_success = true;
|
|
Print("验证完成:所有空头订单已成功平仓");
|
|
} else {
|
|
Print("警告:仍有", remaining_positions, "个空头订单未能平仓!");
|
|
Print("这些订单将继续保留,EA不会重置马丁状态");
|
|
return; // 不重置状态,等待下次检查
|
|
}
|
|
}
|
|
if(close_success) {
|
|
is_short_martin_active = false;
|
|
short_last_entry_price = 0.0;
|
|
short_current_lot_size = Initial_Lots;
|
|
short_trailing_stop_level = 0.0;
|
|
short_highest_profit = 0.0;
|
|
short_martin_add_count = 0;
|
|
// 重置大仓位模式状态
|
|
short_large_position_mode = false;
|
|
short_price_track_level = 0.0;
|
|
// 重置DEMA风险控制状态
|
|
short_dema_risk_active = false;
|
|
short_dema_pause = false;
|
|
short_max_lots = 0.0;
|
|
Print("空头马丁状态已重置,等待重新启动");
|
|
Print("空头马丁平仓完成,等待", Restart_Delay_Seconds, "秒后重启检查");
|
|
}
|
|
}
|
|
|
|
//+------------------------------------------------------------------+
|
|
//| 获取多头持仓数 |
|
|
//+------------------------------------------------------------------+
|
|
int GetLongPositionCount() {
|
|
int count = 0;
|
|
for(int i = 0; i < PositionsTotal(); i++) {
|
|
ulong ticket = PositionGetTicket(i);
|
|
if(PositionSelectByTicket(ticket)) {
|
|
if(PositionGetString(POSITION_SYMBOL) == _Symbol &&
|
|
PositionGetInteger(POSITION_MAGIC) == Magic_Number &&
|
|
PositionGetInteger(POSITION_TYPE) == POSITION_TYPE_BUY) {
|
|
count++;
|
|
}
|
|
}
|
|
}
|
|
return count;
|
|
}
|
|
|
|
//+------------------------------------------------------------------+
|
|
//| 获取空头持仓数 |
|
|
//+------------------------------------------------------------------+
|
|
int GetShortPositionCount() {
|
|
int count = 0;
|
|
for(int i = 0; i < PositionsTotal(); i++) {
|
|
ulong ticket = PositionGetTicket(i);
|
|
if(PositionSelectByTicket(ticket)) {
|
|
if(PositionGetString(POSITION_SYMBOL) == _Symbol &&
|
|
PositionGetInteger(POSITION_MAGIC) == Magic_Number &&
|
|
PositionGetInteger(POSITION_TYPE) == POSITION_TYPE_SELL) {
|
|
count++;
|
|
}
|
|
}
|
|
}
|
|
return count;
|
|
}
|
|
|
|
//+------------------------------------------------------------------+
|
|
//| 计算多头总盈利 |
|
|
//+------------------------------------------------------------------+
|
|
double GetLongTotalProfit() {
|
|
double total_profit = 0.0;
|
|
|
|
for(int i = 0; i < PositionsTotal(); i++) {
|
|
ulong ticket = PositionGetTicket(i);
|
|
if(PositionSelectByTicket(ticket)) {
|
|
if(PositionGetString(POSITION_SYMBOL) == _Symbol &&
|
|
PositionGetInteger(POSITION_MAGIC) == Magic_Number &&
|
|
PositionGetInteger(POSITION_TYPE) == POSITION_TYPE_BUY) {
|
|
total_profit += PositionGetDouble(POSITION_PROFIT);
|
|
}
|
|
}
|
|
}
|
|
|
|
return total_profit;
|
|
}
|
|
|
|
//+------------------------------------------------------------------+
|
|
//| 计算空头总盈利 |
|
|
//+------------------------------------------------------------------+
|
|
double GetShortTotalProfit() {
|
|
double total_profit = 0.0;
|
|
|
|
for(int i = 0; i < PositionsTotal(); i++) {
|
|
ulong ticket = PositionGetTicket(i);
|
|
if(PositionSelectByTicket(ticket)) {
|
|
if(PositionGetString(POSITION_SYMBOL) == _Symbol &&
|
|
PositionGetInteger(POSITION_MAGIC) == Magic_Number &&
|
|
PositionGetInteger(POSITION_TYPE) == POSITION_TYPE_SELL) {
|
|
total_profit += PositionGetDouble(POSITION_PROFIT);
|
|
}
|
|
}
|
|
}
|
|
|
|
return total_profit;
|
|
}
|
|
|
|
//+------------------------------------------------------------------+
|
|
//| 计算多头总仓位大小 |
|
|
//+------------------------------------------------------------------+
|
|
double GetLongTotalLots() {
|
|
double total_lots = 0.0;
|
|
|
|
for(int i = 0; i < PositionsTotal(); i++) {
|
|
ulong ticket = PositionGetTicket(i);
|
|
if(PositionSelectByTicket(ticket)) {
|
|
if(PositionGetString(POSITION_SYMBOL) == _Symbol &&
|
|
PositionGetInteger(POSITION_MAGIC) == Magic_Number &&
|
|
PositionGetInteger(POSITION_TYPE) == POSITION_TYPE_BUY) {
|
|
total_lots += PositionGetDouble(POSITION_VOLUME);
|
|
}
|
|
}
|
|
}
|
|
|
|
return total_lots;
|
|
}
|
|
|
|
//+------------------------------------------------------------------+
|
|
//| 计算空头总仓位大小 |
|
|
//+------------------------------------------------------------------+
|
|
double GetShortTotalLots() {
|
|
double total_lots = 0.0;
|
|
|
|
for(int i = 0; i < PositionsTotal(); i++) {
|
|
ulong ticket = PositionGetTicket(i);
|
|
if(PositionSelectByTicket(ticket)) {
|
|
if(PositionGetString(POSITION_SYMBOL) == _Symbol &&
|
|
PositionGetInteger(POSITION_MAGIC) == Magic_Number &&
|
|
PositionGetInteger(POSITION_TYPE) == POSITION_TYPE_SELL) {
|
|
total_lots += PositionGetDouble(POSITION_VOLUME);
|
|
}
|
|
}
|
|
}
|
|
|
|
return total_lots;
|
|
}
|
|
|
|
//+------------------------------------------------------------------+
|
|
//| 在图表上显示信息 |
|
|
//+------------------------------------------------------------------+
|
|
void OnChartEvent(const int id, const long &lparam, const double &dparam, const string &sparam) {
|
|
// 在图表右上角显示策略状态
|
|
string info = "";
|
|
info += "XAUUSD双向马丁EA\n";
|
|
info += "魔术号: " + IntegerToString(Magic_Number) + "\n";
|
|
info += "价格阈值: $" + DoubleToString(Price_Threshold, 2) + "\n";
|
|
info += "盈利目标: $" + DoubleToString(Profit_Target, 2) + "\n";
|
|
info += "保护比例: " + DoubleToString(Profit_Protection_Ratio * 100, 0) + "%\n";
|
|
info += "多头马丁: " + (Enable_Long_Martin ? "启用" : "禁用") + "\n";
|
|
info += "空头马丁: " + (Enable_Short_Martin ? "启用" : "禁用") + "\n";
|
|
info += "ATR设置: 周期=" + IntegerToString(ATR_Period) + ", 阈值=" + DoubleToString(ATR_Threshold, 2) + ", 系数=" + DoubleToString(ATR_Distance_Multiplier, 2) + "\n";
|
|
// 当前ATR值
|
|
if(atr_handle != INVALID_HANDLE) {
|
|
double atr_values_display[1];
|
|
if(CopyBuffer(atr_handle, 0, 0, 1, atr_values_display) == 1) {
|
|
info += "当前ATR: $" + DoubleToString(atr_values_display[0], 2) + "\n";
|
|
}
|
|
}
|
|
|
|
// 显示DEMA设置和状态
|
|
if(Enable_DEMA_Risk_Control) {
|
|
info += "DEMA风险控制: 已启用 (短周期=" + IntegerToString(DEMA_Short_Period) + ", 长周期=" + IntegerToString(DEMA_Long_Period) + ", 手数阈值=" + DoubleToString(DEMA_Risk_Lot_Threshold, 2) + ", 差值阈值=" + DoubleToString(DEMA_Difference_Threshold, 2) + ")\n";
|
|
if(dema_short_handle != INVALID_HANDLE && dema_long_handle != INVALID_HANDLE) {
|
|
double dema_short_display[1];
|
|
double dema_long_display[1];
|
|
if(CopyBuffer(dema_short_handle, 0, 0, 1, dema_short_display) == 1 &&
|
|
CopyBuffer(dema_long_handle, 0, 0, 1, dema_long_display) == 1) {
|
|
info += "当前DEMA: 短周期=" + DoubleToString(dema_short_display[0], _Digits) +
|
|
", 长周期=" + DoubleToString(dema_long_display[0], _Digits) + "\n";
|
|
|
|
if(long_dema_risk_active || short_dema_risk_active) {
|
|
if(long_dema_pause)
|
|
info += "多头DEMA状态: 暂停加仓\n";
|
|
else if(long_dema_risk_active)
|
|
info += "多头DEMA状态: 允许加仓\n";
|
|
|
|
if(short_dema_pause)
|
|
info += "空头DEMA状态: 暂停加仓\n";
|
|
else if(short_dema_risk_active)
|
|
info += "空头DEMA状态: 允许加仓\n";
|
|
} else {
|
|
info += "DEMA风险控制: 未激活\n";
|
|
}
|
|
}
|
|
}
|
|
} else {
|
|
info += "DEMA风险控制: 已禁用\n";
|
|
}
|
|
|
|
// 显示ADX趋势过滤状态
|
|
if(Enable_ADX_Filter) {
|
|
info += "ADX趋势过滤: 已启用 (周期=" + IntegerToString(ADX_Period) + ", 阈值=" + DoubleToString(ADX_Threshold, 1) + ")\n";
|
|
if(adx_handle != INVALID_HANDLE) {
|
|
double adx_values_display[1];
|
|
double plus_di_values_display[1];
|
|
double minus_di_values_display[1];
|
|
if(CopyBuffer(adx_handle, 0, 0, 1, adx_values_display) == 1 &&
|
|
CopyBuffer(adx_handle, 1, 0, 1, plus_di_values_display) == 1 &&
|
|
CopyBuffer(adx_handle, 2, 0, 1, minus_di_values_display) == 1) {
|
|
info += "当前ADX: " + DoubleToString(adx_values_display[0], 2) +
|
|
",+DI: " + DoubleToString(plus_di_values_display[0], 2) +
|
|
",-DI: " + DoubleToString(minus_di_values_display[0], 2) + "\n";
|
|
|
|
if(adx_trend_filter_active) {
|
|
if(is_uptrend)
|
|
info += "趋势状态: 上升趋势 (阻止空头加仓)\n";
|
|
else if(is_downtrend)
|
|
info += "趋势状态: 下降趋势 (阻止多头加仓)\n";
|
|
else
|
|
info += "趋势状态: 不明确 (允许双向加仓)\n";
|
|
} else {
|
|
info += "趋势状态: 无强趋势 (允许双向加仓)\n";
|
|
}
|
|
}
|
|
}
|
|
} else {
|
|
info += "ADX趋势过滤: 已禁用\n";
|
|
}
|
|
|
|
// 显示时间过滤状态
|
|
if(Enable_Time_Filter) {
|
|
bool in_trading_time = IsInTradingTime();
|
|
info += "时间过滤: " + (in_trading_time ? "交易时间" : "非交易时间") + "\n";
|
|
info += "交易时段: " + IntegerToString(Trade_Start_Hour) + ":00-" + IntegerToString(Trade_End_Hour) + ":00\n";
|
|
} else {
|
|
info += "时间过滤: 已禁用\n";
|
|
}
|
|
|
|
info += "\n=== 多头马丁 ===";
|
|
info += "\n状态: " + (is_long_martin_active ? "活跃" : "等待") + "\n";
|
|
if(is_long_martin_active) {
|
|
double long_total_lots = GetLongTotalLots();
|
|
info += "总仓位: " + DoubleToString(long_total_lots, 2) + "手\n";
|
|
info += "当前手数: " + DoubleToString(long_current_lot_size, 2) + "\n";
|
|
info += "最大手数: " + DoubleToString(long_max_lots, 2) + "\n";
|
|
info += "加仓次数: " + IntegerToString(long_martin_add_count) + "\n";
|
|
info += "总盈利: $" + DoubleToString(GetLongTotalProfit(), 2) + "\n";
|
|
info += "大仓位模式: " + (long_large_position_mode ? "已激活" : "未激活") + "\n";
|
|
if(long_large_position_mode && long_price_track_level > 0) {
|
|
info += "价格追踪: " + DoubleToString(long_price_track_level, _Digits) + "\n";
|
|
} else if(long_trailing_stop_level > 0) {
|
|
info += "保护线: $" + DoubleToString(long_trailing_stop_level, 2) + "\n";
|
|
}
|
|
}
|
|
|
|
info += "\n=== 空头马丁 ===";
|
|
info += "\n状态: " + (is_short_martin_active ? "活跃" : "等待") + "\n";
|
|
if(is_short_martin_active) {
|
|
double short_total_lots = GetShortTotalLots();
|
|
info += "总仓位: " + DoubleToString(short_total_lots, 2) + "手\n";
|
|
info += "当前手数: " + DoubleToString(short_current_lot_size, 2) + "\n";
|
|
info += "最大手数: " + DoubleToString(short_max_lots, 2) + "\n";
|
|
info += "加仓次数: " + IntegerToString(short_martin_add_count) + "\n";
|
|
info += "总盈利: $" + DoubleToString(GetShortTotalProfit(), 2) + "\n";
|
|
info += "大仓位模式: " + (short_large_position_mode ? "已激活" : "未激活") + "\n";
|
|
if(short_large_position_mode && short_price_track_level > 0) {
|
|
info += "价格追踪: " + DoubleToString(short_price_track_level, _Digits) + "\n";
|
|
} else if(short_trailing_stop_level > 0) {
|
|
info += "保护线: $" + DoubleToString(short_trailing_stop_level, 2) + "\n";
|
|
}
|
|
}
|
|
|
|
// 显示当前价格信息
|
|
double current_price = SymbolInfoDouble(_Symbol, SYMBOL_BID);
|
|
info += "\n当前价格: " + DoubleToString(current_price, _Digits) + "\n";
|
|
if(last_check_price > 0) {
|
|
double price_change = current_price - last_check_price;
|
|
info += "价格变动: " + DoubleToString(price_change, _Digits) + "\n";
|
|
}
|
|
|
|
Comment(info);
|
|
}
|
|
|
|
//+------------------------------------------------------------------+
|
|
//| 批量平仓多头持仓 |
|
|
//+------------------------------------------------------------------+
|
|
bool BatchCloseLongPositions() {
|
|
int positions_to_close = 0;
|
|
ulong tickets[];
|
|
|
|
// 首先收集所有需要平仓的多头订单号
|
|
for(int i = 0; i < PositionsTotal(); i++) {
|
|
ulong ticket = PositionGetTicket(i);
|
|
if(PositionSelectByTicket(ticket)) {
|
|
if(PositionGetString(POSITION_SYMBOL) == _Symbol &&
|
|
PositionGetInteger(POSITION_MAGIC) == Magic_Number &&
|
|
PositionGetInteger(POSITION_TYPE) == POSITION_TYPE_BUY) {
|
|
positions_to_close++;
|
|
ArrayResize(tickets, positions_to_close);
|
|
tickets[positions_to_close - 1] = ticket;
|
|
}
|
|
}
|
|
}
|
|
|
|
if(positions_to_close == 0) {
|
|
Print("没有找到需要平仓的多头订单");
|
|
return true;
|
|
}
|
|
|
|
Print("准备批量平仓", positions_to_close, "个多头订单");
|
|
|
|
// 逐个平仓(因为MQL5没有真正的批量平仓API)
|
|
int success_count = 0;
|
|
for(int i = 0; i < positions_to_close; i++) {
|
|
if(trade.PositionClose(tickets[i])) {
|
|
success_count++;
|
|
} else {
|
|
Print("平仓多头订单失败: ", tickets[i], ", 错误: ", trade.ResultRetcode());
|
|
}
|
|
}
|
|
|
|
Print("批量平仓完成,成功: ", success_count, "/", positions_to_close);
|
|
return (success_count == positions_to_close);
|
|
}
|
|
|
|
//+------------------------------------------------------------------+
|
|
//| 批量平仓空头持仓 |
|
|
//+------------------------------------------------------------------+
|
|
bool BatchCloseShortPositions() {
|
|
int positions_to_close = 0;
|
|
ulong tickets[];
|
|
|
|
// 首先收集所有需要平仓的空头订单号
|
|
for(int i = 0; i < PositionsTotal(); i++) {
|
|
ulong ticket = PositionGetTicket(i);
|
|
if(PositionSelectByTicket(ticket)) {
|
|
if(PositionGetString(POSITION_SYMBOL) == _Symbol &&
|
|
PositionGetInteger(POSITION_MAGIC) == Magic_Number &&
|
|
PositionGetInteger(POSITION_TYPE) == POSITION_TYPE_SELL) {
|
|
positions_to_close++;
|
|
ArrayResize(tickets, positions_to_close);
|
|
tickets[positions_to_close - 1] = ticket;
|
|
}
|
|
}
|
|
}
|
|
|
|
if(positions_to_close == 0) {
|
|
Print("没有找到需要平仓的空头订单");
|
|
return true;
|
|
}
|
|
|
|
Print("准备批量平仓", positions_to_close, "个空头订单");
|
|
|
|
// 逐个平仓(因为MQL5没有真正的批量平仓API)
|
|
int success_count = 0;
|
|
for(int i = 0; i < positions_to_close; i++) {
|
|
if(trade.PositionClose(tickets[i])) {
|
|
success_count++;
|
|
} else {
|
|
Print("平仓空头订单失败: ", tickets[i], ", 错误: ", trade.ResultRetcode());
|
|
}
|
|
}
|
|
|
|
Print("批量平仓完成,成功: ", success_count, "/", positions_to_close);
|
|
return (success_count == positions_to_close);
|
|
}
|
|
|
|
//+------------------------------------------------------------------+
|
|
//| 提交异步平仓 - 多头 |
|
|
//+------------------------------------------------------------------+
|
|
void SubmitAsyncCloseLong() {
|
|
trade.SetAsyncMode(true);
|
|
int submit_count = 0;
|
|
for(int i = PositionsTotal() - 1; i >= 0; i--) {
|
|
ulong ticket = PositionGetTicket(i);
|
|
if(PositionSelectByTicket(ticket)) {
|
|
if(PositionGetString(POSITION_SYMBOL) == _Symbol &&
|
|
PositionGetInteger(POSITION_MAGIC) == Magic_Number &&
|
|
PositionGetInteger(POSITION_TYPE) == POSITION_TYPE_BUY) {
|
|
if(trade.PositionClose(ticket)) {
|
|
submit_count++;
|
|
} else {
|
|
Print("重试提交多头异步平仓失败: ", ticket, ", 错误: ", trade.ResultRetcode());
|
|
}
|
|
}
|
|
}
|
|
}
|
|
trade.SetAsyncMode(false);
|
|
Print("已重试提交多头异步平仓,提交数量=", submit_count);
|
|
}
|
|
|
|
//+------------------------------------------------------------------+
|
|
//| 提交异步平仓 - 空头 |
|
|
//+------------------------------------------------------------------+
|
|
void SubmitAsyncCloseShort() {
|
|
trade.SetAsyncMode(true);
|
|
int submit_count = 0;
|
|
for(int i = PositionsTotal() - 1; i >= 0; i--) {
|
|
ulong ticket = PositionGetTicket(i);
|
|
if(PositionSelectByTicket(ticket)) {
|
|
if(PositionGetString(POSITION_SYMBOL) == _Symbol &&
|
|
PositionGetInteger(POSITION_MAGIC) == Magic_Number &&
|
|
PositionGetInteger(POSITION_TYPE) == POSITION_TYPE_SELL) {
|
|
if(trade.PositionClose(ticket)) {
|
|
submit_count++;
|
|
} else {
|
|
Print("重试提交空头异步平仓失败: ", ticket, ", 错误: ", trade.ResultRetcode());
|
|
}
|
|
}
|
|
}
|
|
}
|
|
trade.SetAsyncMode(false);
|
|
Print("已重试提交空头异步平仓,提交数量=", submit_count);
|
|
}
|
|
|
|
//+------------------------------------------------------------------+
|
|
//| 超时回退:同步强制平仓 - 多头 |
|
|
//+------------------------------------------------------------------+
|
|
void ForceSyncCloseLong() {
|
|
trade.SetAsyncMode(false);
|
|
int positions_closed = 0;
|
|
for(int i = PositionsTotal() - 1; i >= 0; i--) {
|
|
ulong ticket = PositionGetTicket(i);
|
|
if(PositionSelectByTicket(ticket)) {
|
|
if(PositionGetString(POSITION_SYMBOL) == _Symbol &&
|
|
PositionGetInteger(POSITION_MAGIC) == Magic_Number &&
|
|
PositionGetInteger(POSITION_TYPE) == POSITION_TYPE_BUY) {
|
|
if(trade.PositionClose(ticket)) {
|
|
positions_closed++;
|
|
} else {
|
|
Print("同步强制平仓多头失败: ", ticket, ", 错误: ", trade.ResultRetcode());
|
|
}
|
|
}
|
|
}
|
|
}
|
|
Print("同步强制平仓多头完成,成功关闭=", positions_closed);
|
|
}
|
|
|
|
//+------------------------------------------------------------------+
|
|
//| 超时回退:同步强制平仓 - 空头 |
|
|
//+------------------------------------------------------------------+
|
|
void ForceSyncCloseShort() {
|
|
trade.SetAsyncMode(false);
|
|
int positions_closed = 0;
|
|
for(int i = PositionsTotal() - 1; i >= 0; i--) {
|
|
ulong ticket = PositionGetTicket(i);
|
|
if(PositionSelectByTicket(ticket)) {
|
|
if(PositionGetString(POSITION_SYMBOL) == _Symbol &&
|
|
PositionGetInteger(POSITION_MAGIC) == Magic_Number &&
|
|
PositionGetInteger(POSITION_TYPE) == POSITION_TYPE_SELL) {
|
|
if(trade.PositionClose(ticket)) {
|
|
positions_closed++;
|
|
} else {
|
|
Print("同步强制平仓空头失败: ", ticket, ", 错误: ", trade.ResultRetcode());
|
|
}
|
|
}
|
|
}
|
|
}
|
|
Print("同步强制平仓空头完成,成功关闭=", positions_closed);
|
|
}
|
|
|
|
//+------------------------------------------------------------------+
|
|
//| 检查DEMA风险控制 |
|
|
//+------------------------------------------------------------------+
|
|
void CheckDEMARiskControl() {
|
|
if(!Enable_DEMA_Risk_Control) return;
|
|
|
|
// 更新最大手数
|
|
UpdateMaxLots();
|
|
|
|
// 检查是否需要激活DEMA风险控制
|
|
if(!long_dema_risk_active && long_max_lots >= DEMA_Risk_Lot_Threshold) {
|
|
long_dema_risk_active = true;
|
|
Print("DEMA风险控制:多头最大手数(", long_max_lots, ")超过阈值(", DEMA_Risk_Lot_Threshold, "),激活DEMA风险控制");
|
|
}
|
|
|
|
if(!short_dema_risk_active && short_max_lots >= DEMA_Risk_Lot_Threshold) {
|
|
short_dema_risk_active = true;
|
|
Print("DEMA风险控制:空头最大手数(", short_max_lots, ")超过阈值(", DEMA_Risk_Lot_Threshold, "),激活DEMA风险控制");
|
|
}
|
|
|
|
// 如果已激活风险控制,在每根K线结束时检查DEMA
|
|
if(long_dema_risk_active || short_dema_risk_active) {
|
|
int current_bar = Bars(_Symbol, _Period);
|
|
datetime current_bar_time = iTime(_Symbol, _Period, 0);
|
|
|
|
// 只在K线结束时检查一次,避免噪音
|
|
if(current_bar_time != last_dema_check_time) {
|
|
last_dema_check_time = current_bar_time;
|
|
CheckDEMASignals();
|
|
}
|
|
}
|
|
}
|
|
|
|
//+------------------------------------------------------------------+
|
|
//| 更新最大手数 |
|
|
//+------------------------------------------------------------------+
|
|
void UpdateMaxLots() {
|
|
long_max_lots = 0.0;
|
|
short_max_lots = 0.0;
|
|
|
|
for(int i = 0; i < PositionsTotal(); i++) {
|
|
ulong ticket = PositionGetTicket(i);
|
|
if(PositionSelectByTicket(ticket)) {
|
|
if(PositionGetString(POSITION_SYMBOL) == _Symbol &&
|
|
PositionGetInteger(POSITION_MAGIC) == Magic_Number) {
|
|
double lot_size = PositionGetDouble(POSITION_VOLUME);
|
|
if(PositionGetInteger(POSITION_TYPE) == POSITION_TYPE_BUY) {
|
|
if(lot_size > long_max_lots)
|
|
long_max_lots = lot_size; // 记录多头最大手数
|
|
} else if(PositionGetInteger(POSITION_TYPE) == POSITION_TYPE_SELL) {
|
|
if(lot_size > short_max_lots)
|
|
short_max_lots = lot_size; // 记录空头最大手数
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
//+------------------------------------------------------------------+
|
|
//| 检查DEMA信号 |
|
|
//+------------------------------------------------------------------+
|
|
void CheckDEMASignals() {
|
|
if(dema_short_handle == INVALID_HANDLE || dema_long_handle == INVALID_HANDLE) {
|
|
Print("DEMA指标句柄无效,无法检查DEMA信号");
|
|
return;
|
|
}
|
|
|
|
double dema_short_values[1];
|
|
double dema_long_values[1];
|
|
|
|
int copied_short = CopyBuffer(dema_short_handle, 0, 0, 1, dema_short_values);
|
|
int copied_long = CopyBuffer(dema_long_handle, 0, 0, 1, dema_long_values);
|
|
|
|
if(copied_short == 1 && copied_long == 1) {
|
|
double dema_short = dema_short_values[0];
|
|
double dema_long = dema_long_values[0];
|
|
|
|
// 检查多头DEMA风险控制
|
|
if(long_dema_risk_active) {
|
|
double long_difference = dema_short - dema_long;
|
|
if(dema_short > dema_long && MathAbs(long_difference) > DEMA_Difference_Threshold) {
|
|
if(long_dema_pause) {
|
|
long_dema_pause = false;
|
|
Print("DEMA风险控制:多头恢复加仓,DEMA", DEMA_Short_Period, "(", dema_short, ") > DEMA", DEMA_Long_Period, "(", dema_long, "),差值=", DoubleToString(long_difference, 2), " > ", DEMA_Difference_Threshold);
|
|
}
|
|
} else {
|
|
if(!long_dema_pause) {
|
|
long_dema_pause = true;
|
|
if(dema_short <= dema_long) {
|
|
Print("DEMA风险控制:多头暂停加仓,DEMA", DEMA_Short_Period, "(", dema_short, ") <= DEMA", DEMA_Long_Period, "(", dema_long, ")");
|
|
} else {
|
|
Print("DEMA风险控制:多头暂停加仓,差值不足,DEMA", DEMA_Short_Period, "(", dema_short, ") > DEMA", DEMA_Long_Period, "(", dema_long, "),差值=", DoubleToString(long_difference, 2), " <= ", DEMA_Difference_Threshold);
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
// 检查空头DEMA风险控制
|
|
if(short_dema_risk_active) {
|
|
double short_difference = dema_long - dema_short;
|
|
if(dema_short < dema_long && MathAbs(short_difference) > DEMA_Difference_Threshold) {
|
|
if(short_dema_pause) {
|
|
short_dema_pause = false;
|
|
Print("DEMA风险控制:空头恢复加仓,DEMA", DEMA_Short_Period, "(", dema_short, ") < DEMA", DEMA_Long_Period, "(", dema_long, "),差值=", DoubleToString(short_difference, 2), " > ", DEMA_Difference_Threshold);
|
|
}
|
|
} else {
|
|
if(!short_dema_pause) {
|
|
short_dema_pause = true;
|
|
if(dema_short >= dema_long) {
|
|
Print("DEMA风险控制:空头暂停加仓,DEMA", DEMA_Short_Period, "(", dema_short, ") >= DEMA", DEMA_Long_Period, "(", dema_long, ")");
|
|
} else {
|
|
Print("DEMA风险控制:空头暂停加仓,差值不足,DEMA", DEMA_Short_Period, "(", dema_short, ") < DEMA", DEMA_Long_Period, "(", dema_long, "),差值=", DoubleToString(short_difference, 2), " <= ", DEMA_Difference_Threshold);
|
|
}
|
|
}
|
|
}
|
|
}
|
|
} else {
|
|
Print("DEMA数据获取失败,错误:短周期=", copied_short, ",长周期=", copied_long);
|
|
}
|
|
}
|
|
//+------------------------------------------------------------------+
|