//+------------------------------------------------------------------+
//| Gold_Chanlun_Enhanced.mq5 |
//| Copyright 2024, MetaQuotes Software Corp. |
//| https://www.mql5.com |
//+------------------------------------------------------------------+
#property copyright "Copyright 2024, MetaQuotes Software Corp."
#property link "https://www.mql5.com"
#property version "7.5"
#property description "增强版黄金缠论策略(H1+M15时间框架验证)"
#include <Trade/Trade.mqh>
#include <Trade/PositionInfo.mqh>
#include <Indicators\Trend.mqh>
#define MODE_LOWER 1
#define MODE_UPPER 0
//--- 输入参数
input double LotSize = 0.1; // 交易手数
input int ATR_Period = 14; // ATR周期
input int FractalBars = 5; // 分型检测范围
input int MinSRDistance = 800; // 最小支撑阻力距离(点)
input int MaxSpread = 200; // 最大允许点差
input bool EnableBuy = true; // 启用买入交易
input bool EnableSell = true; // 启用卖出交易
input int EntryBuffer = 150; // 入场缓冲区域(点)
input bool EnableDebug = true; // 启用调试日志
input bool UseMultiTimeframe = true; // 使用多时间框架确认
input bool UseVolumeConfirmation = true; // 使用成交量确认
input int EMA_Period = 200; // EMA周期(0=禁用)
input double MinVolumeRatio = 1.5; // 最小成交量比率
//--- 全局变量
double validSupport = 0;
double validResistance = 0;
datetime lastBarTime;
int fractalHandle;
int atrHandle;
int emaHandle;
int volumeHandle;
CTrade trade;
CPositionInfo positionInfo;
double pointValue;
string tradeComment = "";
bool tradeExecutedThisBar = false;
double lastBid = 0;
double lastAsk = 0;
int srZeroCount = 0;
int dynamicMinDistance;
int emergencyTradeTrigger = 0;
double m15Support = 0; // 改为M15支撑位
double m15Resistance = 0; // 改为M15阻力位
datetime lastM15Update; // M15更新时间
//+------------------------------------------------------------------+
//| 专家初始化函数 |
//+------------------------------------------------------------------+
int OnInit()
{
pointValue = SymbolInfoDouble(_Symbol, SYMBOL_POINT);
dynamicMinDistance = MinSRDistance;
// 初始化分型指标
fractalHandle = iFractals(_Symbol, _Period);
if(fractalHandle == INVALID_HANDLE) {
Print("分型指标初始化失败! 错误代码: ", GetLastError());
return(INIT_FAILED);
}
// 初始化ATR指标
atrHandle = iATR(_Symbol, _Period, ATR_Period);
if(atrHandle == INVALID_HANDLE) {
Print("ATR指标初始化失败! 错误代码: ", GetLastError());
return(INIT_FAILED);
}
// 初始化EMA指标(如果启用)
if(EMA_Period > 0) {
emaHandle = iMA(_Symbol, _Period, EMA_Period, 0, MODE_EMA, PRICE_CLOSE);
if(emaHandle == INVALID_HANDLE) {
Print("EMA指标初始化失败! 错误代码: ", GetLastError());
// 非关键指标,不返回失败
}
}
// 初始化成交量指标
volumeHandle = iVolumes(_Symbol, _Period, VOLUME_TICK);
if(volumeHandle == INVALID_HANDLE) {
Print("成交量指标初始化失败! 错误代码: ", GetLastError());
}
// 初始寻找支撑阻力位
FindSupportResistance();
lastBarTime = iTime(_Symbol, _Period, 0);
// 初始更新M15水平
UpdateM15Levels();
lastM15Update = TimeCurrent();
trade.SetExpertMagicNumber(12345);
trade.SetDeviationInPoints(200);
trade.SetTypeFilling(ORDER_FILLING_FOK);
Print("策略初始化成功! 点值: ", pointValue,
" 账户货币: ", AccountInfoString(ACCOUNT_CURRENCY),
" 支撑位: ", validSupport, " 阻力位: ", validResistance);
return(INIT_SUCCEEDED);
}
//+------------------------------------------------------------------+
//| 逆初始化函数 |
//+------------------------------------------------------------------+
void OnDeinit(const int reason)
{
if(fractalHandle != INVALID_HANDLE) IndicatorRelease(fractalHandle);
if(atrHandle != INVALID_HANDLE) IndicatorRelease(atrHandle);
if(emaHandle != INVALID_HANDLE) IndicatorRelease(emaHandle);
if(volumeHandle != INVALID_HANDLE) IndicatorRelease(volumeHandle);
Comment("");
}
//+------------------------------------------------------------------+
//| 主函数 |
//+------------------------------------------------------------------+
void OnTick()
{
lastBid = SymbolInfoDouble(_Symbol, SYMBOL_BID);
lastAsk = SymbolInfoDouble(_Symbol, SYMBOL_ASK);
int currentSpread = (int)MathRound((lastAsk - lastBid)/pointValue);
if(currentSpread > MaxSpread) {
Comment("点差过高: ", currentSpread, " > ", MaxSpread, " ",
"支撑位: ", validSupport, " | 阻力位: ", validResistance);
return;
}
datetime currentBarTime = iTime(_Symbol, _Period, 0);
if(currentBarTime != lastBarTime)
{
FindSupportResistance();
lastBarTime = currentBarTime;
tradeExecutedThisBar = false;
DebugLog("新K线开始: " + TimeToString(currentBarTime));
// 动态调整距离阈值
if(srZeroCount >= 3 && dynamicMinDistance > 50) {
dynamicMinDistance = MathMax(50, dynamicMinDistance - 50);
DebugLog("动态降低距离要求至: " + IntegerToString(dynamicMinDistance));
}
else if(srZeroCount == 0 && dynamicMinDistance < MinSRDistance) {
dynamicMinDistance = MathMin(MinSRDistance, dynamicMinDistance + 50);
DebugLog("动态恢复距离要求至: " + IntegerToString(dynamicMinDistance));
}
}
// 每5分钟更新一次M15支撑阻力
if(UseMultiTimeframe && (TimeCurrent() - lastM15Update) >= 300) {
UpdateM15Levels();
lastM15Update = TimeCurrent();
}
CheckForEntry();
DisplayStatus();
}
//+------------------------------------------------------------------+
//| 更新M15时间框架支撑阻力位 |
//+------------------------------------------------------------------+
void UpdateM15Levels()
{
int m15Fractal = iFractals(_Symbol, PERIOD_M15);
if(m15Fractal == INVALID_HANDLE) {
DebugLog("无法获取M15分型句柄");
return;
}
double lowerFractals[], upperFractals[];
ArraySetAsSeries(lowerFractals, true);
ArraySetAsSeries(upperFractals, true);
// 获取最近10个M15分型
if(CopyBuffer(m15Fractal, MODE_LOWER, 0, 10, lowerFractals) > 0) {
for(int i = 1; i < 10; i++) { // 跳过最近的分型(可能未完成)
if(lowerFractals[i] > 0 && IsValidFractal(lowerFractals, i, MODE_LOWER, PERIOD_M15)) {
m15Support = lowerFractals[i];
break;
}
}
}
if(CopyBuffer(m15Fractal, MODE_UPPER, 0, 10, upperFractals) > 0) {
for(int i = 1; i < 10; i++) {
if(upperFractals[i] > 0 && IsValidFractal(upperFractals, i, MODE_UPPER, PERIOD_M15)) {
m15Resistance = upperFractals[i];
break;
}
}
}
IndicatorRelease(m15Fractal);
DebugLog("M15支撑位: " + DoubleToString(m15Support) + " | M15阻力位: " + DoubleToString(m15Resistance));
}
//+------------------------------------------------------------------+
//| 严格分型验证函数 |
//+------------------------------------------------------------------+
bool IsValidFractal(double &fractals[], int index, int mode, ENUM_TIMEFRAMES tf)
{
// 获取所需K线数据
double open[], high[], low[], close[];
int bars = 3; // 需要3根K线验证分型
int start = index - 1; // 分型在中间位置
if(CopyOpen(_Symbol, tf, start, bars, open) < bars) return false;
if(CopyHigh(_Symbol, tf, start, bars, high) < bars) return false;
if(CopyLow(_Symbol, tf, start, bars, low) < bars) return false;
if(CopyClose(_Symbol, tf, start, bars, close) < bars) return false;
// 下分型验证 (模式 = MODE_LOWER)
if(mode == MODE_LOWER) {
// 中间K线必须有最低的低点
if(low[1] > low[0] || low[1] > low[2]) return false;
// 两侧K线必须有较高的低点
if(low[0] >= low[1] || low[2] >= low[1]) return false;
// 中间K线高点必须低于两侧K线高点
if(high[1] > high[0] || high[1] > high[2]) return false;
return true;
}
// 上分型验证 (模式 = MODE_UPPER)
else {
// 中间K线必须有最高的高点
if(high[1] < high[0] || high[1] < high[2]) return false;
// 两侧K线必须有较低的高点
if(high[0] <= high[1] || high[2] <= high[1]) return false;
// 中间K线低点必须高于两侧K线低点
if(low[1] < low[0] || low[1] < low[2]) return false;
return true;
}
}
//+------------------------------------------------------------------+
//| 成交量确认函数 |
//+------------------------------------------------------------------+
bool VolumeConfirmation(double price, int barsToCheck)
{
if(!UseVolumeConfirmation) return true;
if(volumeHandle == INVALID_HANDLE) return true;
double volumes[];
ArraySetAsSeries(volumes, true);
if(CopyBuffer(volumeHandle, 0, 0, barsToCheck, volumes) < barsToCheck) return true;
double sumVolume = 0;
int count = 0;
for(int i = 0; i < barsToCheck; i++) {
if(volumes[i] > 0) {
sumVolume += volumes[i];
count++;
}
}
if(count == 0) return true;
double avgVolume = sumVolume / count;
double priceRange = 100 * pointValue; // 价格范围100点
double sumConfirmationVolume = 0;
int confirmationCount = 0;
for(int i = 0; i < barsToCheck; i++) {
if(MathAbs(iClose(_Symbol, _Period, i) - price) <= priceRange) {
if(volumes[i] > avgVolume * MinVolumeRatio) {
return true; // 找到确认K线
}
}
}
return false;
}
//+------------------------------------------------------------------+
//| 寻找支撑阻力位(增强版) |
//+------------------------------------------------------------------+
void FindSupportResistance()
{
double currentSupport = 0;
double currentResistance = 0;
double emaValue = 0;
bool useEMA = false;
// 获取EMA值(如果可用)
if(emaHandle != INVALID_HANDLE) {
double ema[1];
if(CopyBuffer(emaHandle, 0, 0, 1, ema) > 0) {
emaValue = ema[0];
useEMA = true;
}
}
// 获取分型数据
double lowerFractals[], upperFractals[];
ArraySetAsSeries(lowerFractals, true);
ArraySetAsSeries(upperFractals, true);
int barsToCheck = FractalBars * 3;
if(CopyBuffer(fractalHandle, MODE_LOWER, 0, barsToCheck, lowerFractals) > 0) {
for(int i = 1; i < barsToCheck; i++) {
if(lowerFractals[i] > 0 && IsValidFractal(lowerFractals, i, MODE_LOWER, _Period)) {
if(VolumeConfirmation(lowerFractals[i], 10)) {
currentSupport = lowerFractals[i];
break;
}
}
}
}
if(CopyBuffer(fractalHandle, MODE_UPPER, 0, barsToCheck, upperFractals) > 0) {
for(int i = 1; i < barsToCheck; i++) {
if(upperFractals[i] > 0 && IsValidFractal(upperFractals, i, MODE_UPPER, _Period)) {
if(VolumeConfirmation(upperFractals[i], 10)) {
currentResistance = upperFractals[i];
break;
}
}
}
}
// 多时间框架确认 (使用H1和M15)
if(UseMultiTimeframe && m15Support > 0 && m15Resistance > 0) {
// 如果当前时间框架未找到有效水平,使用M15水平
if(currentSupport <= 0) currentSupport = m15Support;
if(currentResistance <= 0) currentResistance = m15Resistance;
// 计算加权平均值 (H1 70% + M15 30%)
if(currentSupport > 0 && m15Support > 0) {
currentSupport = (currentSupport * 0.7) + (m15Support * 0.3);
}
if(currentResistance > 0 && m15Resistance > 0) {
currentResistance = (currentResistance * 0.7) + (m15Resistance * 0.3);
}
}
// 最终检查
if(currentSupport > 0 && currentResistance > 0) {
double distance = (currentResistance - currentSupport)/pointValue;
if(distance > dynamicMinDistance) {
validSupport = currentSupport;
validResistance = currentResistance;
srZeroCount = 0;
DebugLog("更新有效支撑位: " + DoubleToString(validSupport) +
" | 阻力位: " + DoubleToString(validResistance) +
" | 距离: " + DoubleToString(distance) + "点");
} else {
DebugLog("支撑阻力距离不足: " + DoubleToString(distance) + "点 < " +
IntegerToString(dynamicMinDistance) + "点");
srZeroCount++;
}
} else {
// 使用EMA作为最后手段
if(useEMA) {
validSupport = emaValue - (300 * pointValue);
validResistance = emaValue + (300 * pointValue);
DebugLog("使用EMA作为支撑阻力: " + DoubleToString(emaValue));
} else {
validSupport = iLow(_Symbol, _Period, iLowest(_Symbol, _Period, MODE_LOW, FractalBars*3, 1));
validResistance = iHigh(_Symbol, _Period, iHighest(_Symbol, _Period, MODE_HIGH, FractalBars*3, 1));
DebugLog("使用价格极值作为支撑阻力");
}
srZeroCount++;
}
}
//+------------------------------------------------------------------+
//| 获取当前ATR值 |
//+------------------------------------------------------------------+
double GetCurrentATR()
{
double atrValue[1];
if(CopyBuffer(atrHandle, 0, 0, 1, atrValue) <= 0) {
Print("获取ATR值失败! 错误: ", GetLastError());
return 0.0;
}
return atrValue[0];
}
//+------------------------------------------------------------------+
//| 交易入场逻辑 |
//+------------------------------------------------------------------+
void CheckForEntry()
{
if(positionInfo.Select(_Symbol)) {
if(tradeComment != "已有持仓") {
tradeComment = "已有持仓,不创建新订单";
DebugLog(tradeComment);
}
return;
}
if(tradeExecutedThisBar) {
tradeComment = "本K线已交易,等待下一根K线";
return;
}
double atrValue = GetCurrentATR();
if(atrValue <= 0) {
tradeComment = "获取ATR值失败,等待下一Tick";
return;
}
// 计算固定止损止盈
double stopLossDistance = 1.8 * atrValue; // 1.8倍ATR止损
double takeProfitDistance = 4.0 * atrValue; // 4.0倍ATR止盈
// 紧急交易机制
if(srZeroCount >= 15 && !tradeExecutedThisBar) {
emergencyTradeTrigger++;
DebugLog("触发紧急交易机制 #" + IntegerToString(emergencyTradeTrigger));
if(EnableBuy && emergencyTradeTrigger % 2 == 0) {
double sl = lastAsk - stopLossDistance;
double tp = lastAsk + takeProfitDistance;
if(trade.Buy(LotSize, _Symbol, lastAsk, sl, tp, "紧急买入")) {
tradeComment = StringFormat("紧急买入成交! 价格: %.3f, SL: %.3f, TP: %.3f", lastAsk, sl, tp);
DebugLog(tradeComment);
tradeExecutedThisBar = true;
srZeroCount = 0;
return;
}
} else if(EnableSell && emergencyTradeTrigger % 2 == 1) {
double sl = lastBid + stopLossDistance;
double tp = lastBid - takeProfitDistance;
if(trade.Sell(LotSize, _Symbol, lastBid, sl, tp, "紧急卖出")) {
tradeComment = StringFormat("紧急卖出成交! 价格: %.3f, SL: %.3f, TP: %.3f", lastBid, sl, tp);
DebugLog(tradeComment);
tradeExecutedThisBar = true;
srZeroCount = 0;
return;
}
}
}
if(validSupport <= 0 || validResistance <= 0) {
tradeComment = "等待有效支撑阻力位...";
return;
}
double bufferPips = EntryBuffer * pointValue;
double buyZoneUpper = validSupport + bufferPips;
double buyZoneLower = validSupport - bufferPips;
double sellZoneUpper = validResistance + bufferPips;
double sellZoneLower = validResistance - bufferPips;
// 买入逻辑
if(EnableBuy && lastBid >= buyZoneLower && lastBid <= buyZoneUpper) {
double sl = lastAsk - stopLossDistance;
double tp = lastAsk + takeProfitDistance;
if(trade.Buy(LotSize, _Symbol, lastAsk, sl, tp, "黄金缠论买入")) {
tradeComment = StringFormat("买入成交! 价格: %.3f, SL: %.3f, TP: %.3f", lastAsk, sl, tp);
DebugLog(tradeComment);
tradeExecutedThisBar = true;
srZeroCount = 0;
} else {
tradeComment = StringFormat("买入失败! 错误: %d", GetLastError());
DebugLog(tradeComment);
}
}
// 卖出逻辑
else if(EnableSell && lastAsk >= sellZoneLower && lastAsk <= sellZoneUpper) {
double sl = lastBid + stopLossDistance;
double tp = lastBid - takeProfitDistance;
if(trade.Sell(LotSize, _Symbol, lastBid, sl, tp, "黄金缠论卖出")) {
tradeComment = StringFormat("卖出成交! 价格: %.3f, SL: %.3f, TP: %.3f", lastBid, sl, tp);
DebugLog(tradeComment);
tradeExecutedThisBar = true;
srZeroCount = 0;
} else {
tradeComment = StringFormat("卖出失败! 错误: %d", GetLastError());
DebugLog(tradeComment);
}
} else {
tradeComment = "等待交易信号...";
if(EnableDebug) {
DebugLog(StringFormat("买价:%.3f 支撑区[%.3f-%.3f] 卖价:%.3f 阻力区[%.3f-%.3f]",
lastBid, buyZoneLower, buyZoneUpper, lastAsk, sellZoneLower, sellZoneUpper));
}
}
}
//+------------------------------------------------------------------+
//| 调试日志输出 |
//+------------------------------------------------------------------+
void DebugLog(string message)
{
if(EnableDebug) Print(TimeToString(TimeCurrent(), TIME_SECONDS), " ", message);
}
//+------------------------------------------------------------------+
//| 显示实时状态 |
//+------------------------------------------------------------------+
void DisplayStatus()
{
string status = StringFormat("支撑位: %.3f | 阻力位: %.3f | 点差: %d ",
validSupport, validResistance, (int)MathRound((lastAsk-lastBid)/pointValue));
status += StringFormat("买价: %.3f | 卖价: %.3f | 时间: %s ",
lastBid, lastAsk, TimeToString(TimeCurrent(), TIME_SECONDS));
status += StringFormat("动态距离阈值: %d点 | 无信号计数: %d ",
dynamicMinDistance, srZeroCount);
status += StringFormat("M15支撑: %.3f | M15阻力: %.3f", m15Support, m15Resistance);
if(validSupport > 0) {
double toSupport = (lastBid - validSupport)/pointValue;
status += StringFormat(" | 距支撑: %.1f点", toSupport);
}
if(validResistance > 0) {
double toResistance = (validResistance - lastAsk)/pointValue;
status += StringFormat(" | 距阻力: %.1f点", toResistance);
}
if(positionInfo.Select(_Symbol)) {
double profit = positionInfo.Profit();
status += StringFormat(" 当前持仓: %s 手数: %.2f 盈亏: $%.2f",
positionInfo.PositionType()==POSITION_TYPE_BUY ? "买入" : "卖出",
positionInfo.Volume(), profit);
}
status += " " + tradeComment;
Comment(status);
}
//+------------------------------------------------------------------+