整理: 新增PriceAction_EngulfingZone_EA, 1h_eur_usd, 改良海龟三个EA; 新增SHK Heikin Ashi指标元数据; 修正文档-文件名保持原命名规则

This commit is contained in:
2026-08-11 21:07:18 +08:00
parent 630b797768
commit cd007dc423
12 changed files with 3164 additions and 18 deletions
+3 -3
View File
@@ -26,7 +26,7 @@ INDEX.md 人类可读的文件索引(每个文件一行,含一句话简介)
2. **不删文件**,废弃 EA 移 `99_Retired/`。
3. **新 EA 一律先落 `90_Experimental/`**,验证通过才升级。
4. **每个文件必须在 `index.csv` 登记一行**,否则视为未完成。
5. **不用中文路径**,命名只用英文和下划线。
5. **不改文件名**,所有文件忠于原命名(无论中文/英文/来源),防止溯源困难。
6. **`.set` 文件与对应 EA 同名**,放 `Presets/`。
## 整理单个文件的流程(新增 / 迁移)
@@ -58,7 +58,7 @@ INDEX.md 人类可读的文件索引(每个文件一行,含一句话简介)
2. **改名副本**(内容相似但文件名不同)单列记录,不合并。
### Step 4 — 命名
检查文件名是否符合 `[资产]_[策略]_[逻辑]_v[版本]`。不符合且是**自研** → 重命名并同步更新文件内 `#property`(如适用)。**外部来源(GitHub/MQL5/购买)保留原名**,在 `index.csv` 注明来源。
**所有文件保持原名,不做任何重命名操作**,无论来源(自研/GitHub/MQL5/购买)、语言(中文/英文)、格式是否符合规范。文件名是溯源的关键标识,改动会导致回测配置、.set 文件、历史记录无法对应。
### Step 5 — 移动文件
用 `Move-Item` 移动到目标目录。**不要复制**。
@@ -143,7 +143,7 @@ INDEX.md 人类可读的文件索引(每个文件一行,含一句话简介)
一个文件的整理**只有同时满足**才算完成:
- [ ] 文件在正确的目标目录
- [ ] 命名符合规范(或外部来源已注明保留原名原因)
- [ ] 文件保持原名,未被重命名
- [ ] `INDEX.md` 有对应记录
- [ ] `Meta/` 有对应元数据文件
- [ ] `Inbox/` 无残留、无复制残留、无 `.ex5` 编译产物入库
+3 -15
View File
@@ -41,21 +41,9 @@
## 2. 命名
```
[资产]_[策略]_[核心逻辑]_v[版本].mq5
```
**所有文件忠于原命名,不做任何重命名操作**,无论来源(自研/GitHub/MQL5/购买)、语言(中文/英文)、格式是否符合规范。
规则:
- 只用英文和下划线
- 不用中文路径(MT5/Git 都有坑)
- 版本号单调递增,不回头
```
FX_Trend_MACross_v2.3.mq5 ← 正确
Multi_Grid_EquityHedge_v1.0.mq5 ← 正确
双均线交易系统.mq5 ← 错误
test.mq5 ← 错误
```
文件名是溯源的关键标识,改动会导致回测配置、.set 文件、历史记录无法对应。整理流程中不涉及任何重命名步骤。
---
@@ -97,7 +85,7 @@ test.mq5 ← 错误
- **不删文件**:废弃 EA 移入 `99_Retired/`,保留历史
- **不重复放**:一个文件只在一个目录里,不要复制到多个位置
- **不改别人的文件名格式**:从 GitHub/MQL5 下载的保留原名,在 `notes` 里注明来源
- **不改文件名**:所有文件忠于原命名,无论是自研还是外部来源(中文/英文/GitHub/MQL5/购买),防止溯源困难
- **.set 文件跟 EA 同名**:否则回测不可复现
- **移动文件后必须同步修复 `#include` 路径**:MQL5 的 `#include "..."` 以当前文件目录为基准,跨目录引用必须用相对路径指向新位置。标准库(`<...>`)不受影响。详见 `AGENTS.md` Step 5b。
- **参数注释使用中文**:所有 `input` 参数注释应为中文,外部来源文件保留英文原文,但 `Meta/` 中提供中文说明。详见 `AGENTS.md` Step 5c。
Binary file not shown.
File diff suppressed because it is too large Load Diff
+695
View File
@@ -0,0 +1,695 @@
//+------------------------------------------------------------------+
//| Improved_Turtle_30M_CN.mq5 |
//| 改良海龟策略:30分钟唐奇安突破 + 趋势筛选 + 动态离场 |
//+------------------------------------------------------------------+
#property copyright "OpenAI"
#property version "1.00"
#property strict
#include <Trade/Trade.mqh>
enum TrendFilterMode
{
TrendFilter_MidlineOnly = 0, // 仅短周期中轨方向过滤
TrendFilter_LongMidline = 1, // 仅长周期中轨方向过滤
TrendFilter_DoubleFilter = 2 // 短周期中轨 + 长周期中轨双重过滤
};
enum ExitAnchorMode
{
ExitAnchor_StandardChandelier = 0, // 标准吊灯:多头最高价-幅度,空头最低价+幅度
ExitAnchor_TextStrict = 1 // 严格文字版:多头最低价-幅度,空头最高价+幅度
};
input group "一、基础设置";
input ENUM_TIMEFRAMES InpSignalTimeframe = PERIOD_M30; // 策略运行周期,推荐30分钟
input ulong InpMagic = 2026080630; // 魔术编号
input int InpDeviationPoints = 20; // 允许滑点,单位:点
input bool InpPrintLog = true; // 是否打印交易日志
input group "二、通道与趋势参数";
input int InpBoLength = 25; // 短周期唐奇安通道参数
input int InpFsLength = 65; // 长周期唐奇安通道参数
input TrendFilterMode InpTrendFilterMode = TrendFilter_DoubleFilter; // 趋势筛选模式
input group "三、动态离场参数";
input double InpTRS = 15.0; // 基础动态离场幅度,按千分比计算
input double InpXX = 0.40; // 大周期趋势启动后的离场收敛系数
input bool InpUseTimeTighten = true; // 是否启用持仓时长自适应收紧
input int InpTightenStartBars= 10; // 持仓多少根K线后开始收紧
input int InpTightenFullBars = 80; // 持仓多少根K线后收紧至最低系数
input double InpMinTightenCoef = 0.30; // 持仓时间收紧最低系数
input ExitAnchorMode InpExitAnchorMode = ExitAnchor_StandardChandelier; // 动态离场极值模式
input group "四、仓位与风控参数";
input bool InpUseRiskLot = true; // 是否按风险比例自动计算手数
input double InpRiskPercent = 1.00; // 单笔理论风险占账户余额比例
input double InpFixedLots = 1.00; // 固定手数
input double InpMinimumLots = 1.00; // 策略最低交易手数
input double InpMaxMarginPercent= 80.0; // 最大可用保证金占用比例
input bool InpAllowBuy = true; // 允许做多
input bool InpAllowSell = true; // 允许做空
CTrade trade;
datetime g_lastSignalBarTime = 0;
//+------------------------------------------------------------------+
//| 初始化 |
//+------------------------------------------------------------------+
int OnInit()
{
if(InpBoLength < 2)
{
Print("初始化失败:短周期唐奇安通道参数必须大于等于2。");
return INIT_PARAMETERS_INCORRECT;
}
if(InpFsLength <= InpBoLength)
{
Print("初始化失败:长周期唐奇安通道参数建议大于短周期参数。");
return INIT_PARAMETERS_INCORRECT;
}
if(InpTRS <= 0.0)
{
Print("初始化失败:基础动态离场幅度必须大于0。");
return INIT_PARAMETERS_INCORRECT;
}
if(InpXX <= 0.0 || InpXX > 1.0)
{
Print("初始化失败:行情收敛系数必须在0到1之间。");
return INIT_PARAMETERS_INCORRECT;
}
if(InpRiskPercent <= 0.0 && InpUseRiskLot)
{
Print("初始化失败:启用风险手数时,风险比例必须大于0。");
return INIT_PARAMETERS_INCORRECT;
}
if(InpFixedLots <= 0.0)
{
Print("初始化失败:固定手数必须大于0。");
return INIT_PARAMETERS_INCORRECT;
}
if(InpMinimumLots <= 0.0)
{
Print("初始化失败:最低交易手数必须大于0。");
return INIT_PARAMETERS_INCORRECT;
}
trade.SetExpertMagicNumber(InpMagic);
trade.SetDeviationInPoints(InpDeviationPoints);
trade.SetTypeFillingBySymbol(_Symbol);
SyncCurrentSignalBar();
if(InpPrintLog)
Print("改良海龟策略初始化完成。品种=", _Symbol, " 周期=", EnumToString(InpSignalTimeframe));
return INIT_SUCCEEDED;
}
//+------------------------------------------------------------------+
//| 释放 |
//+------------------------------------------------------------------+
void OnDeinit(const int reason)
{
if(InpPrintLog)
Print("改良海龟策略已停止。停止原因=", reason);
}
//+------------------------------------------------------------------+
//| 每个Tick执行 |
//+------------------------------------------------------------------+
void OnTick()
{
if(!IsTradeEnvironmentReady())
return;
bool closed = ManageOpenPosition();
if(closed)
return;
if(IsNewSignalBar())
ProcessEntrySignal();
}
//+------------------------------------------------------------------+
//| 同步当前K线时间,避免EA加载瞬间追单 |
//+------------------------------------------------------------------+
void SyncCurrentSignalBar()
{
datetime times[];
ArraySetAsSeries(times, true);
int copied = CopyTime(_Symbol, InpSignalTimeframe, 0, 1, times);
if(copied == 1)
g_lastSignalBarTime = times[0];
}
//+------------------------------------------------------------------+
//| 判断是否出现新K线 |
//+------------------------------------------------------------------+
bool IsNewSignalBar()
{
datetime times[];
ArraySetAsSeries(times, true);
int copied = CopyTime(_Symbol, InpSignalTimeframe, 0, 1, times);
if(copied != 1)
return false;
if(g_lastSignalBarTime == 0)
{
g_lastSignalBarTime = times[0];
return false;
}
if(times[0] != g_lastSignalBarTime)
{
g_lastSignalBarTime = times[0];
return true;
}
return false;
}
//+------------------------------------------------------------------+
//| 交易环境检查 |
//+------------------------------------------------------------------+
bool IsTradeEnvironmentReady()
{
if(TerminalInfoInteger(TERMINAL_TRADE_ALLOWED) == 0)
return false;
if(MQLInfoInteger(MQL_TRADE_ALLOWED) == 0)
return false;
if(SymbolInfoInteger(_Symbol, SYMBOL_TRADE_MODE) == SYMBOL_TRADE_MODE_DISABLED)
return false;
return true;
}
//+------------------------------------------------------------------+
//| 入场信号处理 |
//+------------------------------------------------------------------+
void ProcessEntrySignal()
{
ulong ticket = 0;
ENUM_POSITION_TYPE posType = POSITION_TYPE_BUY;
double posVolume = 0.0;
double posOpenPrice = 0.0;
datetime posOpenTime = 0;
if(GetOwnPosition(ticket, posType, posVolume, posOpenPrice, posOpenTime))
return;
int requiredBars = MathMax(InpBoLength, InpFsLength) + 5;
if(Bars(_Symbol, InpSignalTimeframe) < requiredBars)
return;
double signalClose = iClose(_Symbol, InpSignalTimeframe, 1);
if(signalClose <= 0.0)
return;
double boUpper = 0.0;
double boLower = 0.0;
double boUpperPrev = 0.0;
double boLowerPrev = 0.0;
double fsUpper = 0.0;
double fsLower = 0.0;
double fsUpperPrev = 0.0;
double fsLowerPrev = 0.0;
if(!GetCloseDonchian(InpBoLength, 2, boUpper, boLower))
return;
if(!GetCloseDonchian(InpBoLength, 3, boUpperPrev, boLowerPrev))
return;
if(!GetCloseDonchian(InpFsLength, 2, fsUpper, fsLower))
return;
if(!GetCloseDonchian(InpFsLength, 3, fsUpperPrev, fsLowerPrev))
return;
bool longBreakout = signalClose > boUpper;
bool shortBreakout = signalClose < boLower;
bool longFilter = TrendPass(true, signalClose, boUpper, boLower, boUpperPrev, boLowerPrev, fsUpper, fsLower, fsUpperPrev, fsLowerPrev);
bool shortFilter = TrendPass(false, signalClose, boUpper, boLower, boUpperPrev, boLowerPrev, fsUpper, fsLower, fsUpperPrev, fsLowerPrev);
MqlTick tick;
if(!SymbolInfoTick(_Symbol, tick))
return;
if(InpAllowBuy && longBreakout && longFilter)
{
double lots = CalculateTradeLots(true, tick.ask);
if(lots > 0.0)
{
bool result = trade.Buy(lots, _Symbol, 0.0, 0.0, 0.0, "Improved Turtle Long");
if(InpPrintLog)
Print("多头开仓信号:lots=", DoubleToString(lots, 2), " close=", DoubleToString(signalClose, _Digits), " result=", result);
}
return;
}
if(InpAllowSell && shortBreakout && shortFilter)
{
double lots = CalculateTradeLots(false, tick.bid);
if(lots > 0.0)
{
bool result = trade.Sell(lots, _Symbol, 0.0, 0.0, 0.0, "Improved Turtle Short");
if(InpPrintLog)
Print("空头开仓信号:lots=", DoubleToString(lots, 2), " close=", DoubleToString(signalClose, _Digits), " result=", result);
}
}
}
//+------------------------------------------------------------------+
//| 持仓管理与动态离场 |
//+------------------------------------------------------------------+
bool ManageOpenPosition()
{
ulong ticket = 0;
ENUM_POSITION_TYPE posType = POSITION_TYPE_BUY;
double posVolume = 0.0;
double posOpenPrice = 0.0;
datetime posOpenTime = 0;
if(!GetOwnPosition(ticket, posType, posVolume, posOpenPrice, posOpenTime))
return false;
MqlTick tick;
if(!SymbolInfoTick(_Symbol, tick))
return false;
double highSinceOpen = 0.0;
double lowSinceOpen = 0.0;
int barsHeld = 0;
if(!GetExtremesSinceOpen(posOpenTime, highSinceOpen, lowSinceOpen, barsHeld))
return false;
bool isLong = (posType == POSITION_TYPE_BUY);
double currentPrice = isLong ? tick.bid : tick.ask;
if(currentPrice <= 0.0)
return false;
bool bigTrend = IsBigTrendActive(isLong, currentPrice);
double distance = CalculateExitDistance(currentPrice, barsHeld, bigTrend);
if(distance <= 0.0)
return false;
double exitLine = 0.0;
if(InpExitAnchorMode == ExitAnchor_StandardChandelier)
{
if(isLong)
exitLine = highSinceOpen - distance;
else
exitLine = lowSinceOpen + distance;
}
else
{
if(isLong)
exitLine = lowSinceOpen - distance;
else
exitLine = highSinceOpen + distance;
}
exitLine = NormalizeDouble(exitLine, _Digits);
bool shouldExit = false;
if(isLong && tick.bid <= exitLine)
shouldExit = true;
if(!isLong && tick.ask >= exitLine)
shouldExit = true;
if(!shouldExit)
return false;
bool closed = trade.PositionClose(ticket, (ulong)InpDeviationPoints);
if(InpPrintLog)
{
string dir = isLong ? "多头" : "空头";
Print(dir, "动态离场触发:ticket=", ticket,
" exitLine=", DoubleToString(exitLine, _Digits),
" price=", DoubleToString(currentPrice, _Digits),
" barsHeld=", barsHeld,
" bigTrend=", bigTrend,
" closed=", closed);
}
return closed;
}
//+------------------------------------------------------------------+
//| 计算收盘价唐奇安通道 |
//+------------------------------------------------------------------+
bool GetCloseDonchian(const int length, const int startShift, double &upper, double &lower)
{
if(length <= 0 || startShift < 0)
return false;
double closes[];
ArraySetAsSeries(closes, true);
int copied = CopyClose(_Symbol, InpSignalTimeframe, startShift, length, closes);
if(copied < length)
return false;
upper = closes[0];
lower = closes[0];
for(int i = 1; i < copied; i++)
{
if(closes[i] > upper)
upper = closes[i];
if(closes[i] < lower)
lower = closes[i];
}
return true;
}
//+------------------------------------------------------------------+
//| 趋势筛选 |
//+------------------------------------------------------------------+
bool TrendPass(const bool isLong,
const double signalClose,
const double boUpper,
const double boLower,
const double boUpperPrev,
const double boLowerPrev,
const double fsUpper,
const double fsLower,
const double fsUpperPrev,
const double fsLowerPrev)
{
double boMid = (boUpper + boLower) / 2.0;
double boMidPrev = (boUpperPrev + boLowerPrev) / 2.0;
double fsMid = (fsUpper + fsLower) / 2.0;
double fsMidPrev = (fsUpperPrev + fsLowerPrev) / 2.0;
bool shortMidPass = false;
bool longMidPass = false;
if(isLong)
{
shortMidPass = (signalClose > boMid && boMid >= boMidPrev);
longMidPass = (signalClose > fsMid && fsMid >= fsMidPrev);
}
else
{
shortMidPass = (signalClose < boMid && boMid <= boMidPrev);
longMidPass = (signalClose < fsMid && fsMid <= fsMidPrev);
}
if(InpTrendFilterMode == TrendFilter_MidlineOnly)
return shortMidPass;
if(InpTrendFilterMode == TrendFilter_LongMidline)
return longMidPass;
return (shortMidPass && longMidPass);
}
//+------------------------------------------------------------------+
//| 判断大周期趋势是否启动 |
//+------------------------------------------------------------------+
bool IsBigTrendActive(const bool isLong, const double currentPrice)
{
double fsUpper = 0.0;
double fsLower = 0.0;
if(!GetCloseDonchian(InpFsLength, 1, fsUpper, fsLower))
return false;
if(isLong && currentPrice > fsUpper)
return true;
if(!isLong && currentPrice < fsLower)
return true;
return false;
}
//+------------------------------------------------------------------+
//| 计算动态离场距离 |
//+------------------------------------------------------------------+
double CalculateExitDistance(const double currentPrice, const int barsHeld, const bool bigTrend)
{
double baseDistance = currentPrice * InpTRS / 1000.0;
double timeCoef = GetTimeTightenCoefficient(barsHeld);
double trendCoef = bigTrend ? InpXX : 1.0;
double distance = baseDistance * timeCoef * trendCoef;
return NormalizeDouble(distance, _Digits);
}
//+------------------------------------------------------------------+
//| 持仓时间收紧系数 |
//+------------------------------------------------------------------+
double GetTimeTightenCoefficient(const int barsHeld)
{
if(!InpUseTimeTighten)
return 1.0;
double minCoef = ClampDouble(InpMinTightenCoef, 0.05, 1.0);
if(barsHeld <= InpTightenStartBars)
return 1.0;
if(InpTightenFullBars <= InpTightenStartBars)
return minCoef;
double progress = (double)(barsHeld - InpTightenStartBars) / (double)(InpTightenFullBars - InpTightenStartBars);
progress = ClampDouble(progress, 0.0, 1.0);
return 1.0 - (1.0 - minCoef) * progress;
}
//+------------------------------------------------------------------+
//| 获取持仓以来最高价、最低价、持仓K线数 |
//+------------------------------------------------------------------+
bool GetExtremesSinceOpen(const datetime openTime, double &highValue, double &lowValue, int &barsHeld)
{
int openShift = iBarShift(_Symbol, InpSignalTimeframe, openTime, false);
if(openShift < 0)
return false;
int barsToCopy = openShift + 1;
if(barsToCopy < 1)
barsToCopy = 1;
MqlRates rates[];
ArraySetAsSeries(rates, true);
int copied = CopyRates(_Symbol, InpSignalTimeframe, 0, barsToCopy, rates);
if(copied <= 0)
return false;
highValue = rates[0].high;
lowValue = rates[0].low;
for(int i = 1; i < copied; i++)
{
if(rates[i].high > highValue)
highValue = rates[i].high;
if(rates[i].low < lowValue)
lowValue = rates[i].low;
}
barsHeld = openShift;
return true;
}
//+------------------------------------------------------------------+
//| 自动计算交易手数 |
//+------------------------------------------------------------------+
double CalculateTradeLots(const bool isLong, const double orderPrice)
{
double lots = InpFixedLots;
if(InpUseRiskLot)
{
double tickSize = SymbolInfoDouble(_Symbol, SYMBOL_TRADE_TICK_SIZE);
double tickValue = SymbolInfoDouble(_Symbol, SYMBOL_TRADE_TICK_VALUE);
if(tickSize <= 0.0 || tickValue <= 0.0 || orderPrice <= 0.0)
return 0.0;
double virtualRiskDistance = orderPrice * InpTRS / 1000.0;
double lossPerLot = virtualRiskDistance / tickSize * tickValue;
if(lossPerLot <= 0.0)
return 0.0;
double balance = AccountInfoDouble(ACCOUNT_BALANCE);
double riskMoney = balance * InpRiskPercent / 100.0;
lots = riskMoney / lossPerLot;
}
lots = MathMax(lots, InpMinimumLots);
lots = NormalizeLots(lots);
lots = AdjustLotsByMargin(isLong, lots, orderPrice);
return lots;
}
//+------------------------------------------------------------------+
//| 根据保证金调整手数 |
//+------------------------------------------------------------------+
double AdjustLotsByMargin(const bool isLong, const double lots, const double price)
{
double volumeMin = SymbolInfoDouble(_Symbol, SYMBOL_VOLUME_MIN);
double volumeMax = SymbolInfoDouble(_Symbol, SYMBOL_VOLUME_MAX);
double volumeStep = SymbolInfoDouble(_Symbol, SYMBOL_VOLUME_STEP);
if(volumeMin <= 0.0)
volumeMin = 0.01;
if(volumeStep <= 0.0)
volumeStep = volumeMin;
double minimumAllowed = MathMax(volumeMin, InpMinimumLots);
double candidate = MathMin(lots, volumeMax);
candidate = NormalizeLots(candidate);
ENUM_ORDER_TYPE orderType = isLong ? ORDER_TYPE_BUY : ORDER_TYPE_SELL;
double freeMargin = AccountInfoDouble(ACCOUNT_MARGIN_FREE);
double maxMargin = freeMargin * ClampDouble(InpMaxMarginPercent, 1.0, 100.0) / 100.0;
for(int i = 0; i < 10000; i++)
{
if(candidate < minimumAllowed)
break;
double margin = 0.0;
bool ok = OrderCalcMargin(orderType, _Symbol, candidate, price, margin);
if(ok && margin > 0.0 && margin <= maxMargin)
return candidate;
candidate -= volumeStep;
candidate = NormalizeLots(candidate);
}
if(InpPrintLog)
Print("手数不足或保证金不足,取消本次开仓。计算手数=", DoubleToString(lots, 2));
return 0.0;
}
//+------------------------------------------------------------------+
//| 规范化手数 |
//+------------------------------------------------------------------+
double NormalizeLots(const double lots)
{
double volumeMin = SymbolInfoDouble(_Symbol, SYMBOL_VOLUME_MIN);
double volumeMax = SymbolInfoDouble(_Symbol, SYMBOL_VOLUME_MAX);
double volumeStep = SymbolInfoDouble(_Symbol, SYMBOL_VOLUME_STEP);
if(volumeMin <= 0.0)
volumeMin = 0.01;
if(volumeMax <= 0.0)
volumeMax = 100.0;
if(volumeStep <= 0.0)
volumeStep = volumeMin;
double value = MathMax(lots, volumeMin);
value = MathMin(value, volumeMax);
value = MathFloor(value / volumeStep + 0.00000001) * volumeStep;
int digits = VolumeDigits(volumeStep);
value = NormalizeDouble(value, digits);
return value;
}
//+------------------------------------------------------------------+
//| 计算手数小数位 |
//+------------------------------------------------------------------+
int VolumeDigits(double step)
{
int digits = 0;
while(step < 1.0 && digits < 8)
{
step *= 10.0;
digits++;
}
return digits;
}
//+------------------------------------------------------------------+
//| 获取当前EA自己的持仓 |
//+------------------------------------------------------------------+
bool GetOwnPosition(ulong &ticket,
ENUM_POSITION_TYPE &type,
double &volume,
double &priceOpen,
datetime &timeOpen)
{
int total = PositionsTotal();
for(int i = total - 1; i >= 0; i--)
{
ulong posTicket = PositionGetTicket(i);
if(posTicket == 0)
continue;
if(!PositionSelectByTicket(posTicket))
continue;
string posSymbol = PositionGetString(POSITION_SYMBOL);
ulong posMagic = (ulong)PositionGetInteger(POSITION_MAGIC);
if(posSymbol != _Symbol)
continue;
if(posMagic != InpMagic)
continue;
ticket = posTicket;
type = (ENUM_POSITION_TYPE)PositionGetInteger(POSITION_TYPE);
volume = PositionGetDouble(POSITION_VOLUME);
priceOpen = PositionGetDouble(POSITION_PRICE_OPEN);
timeOpen = (datetime)PositionGetInteger(POSITION_TIME);
return true;
}
return false;
}
//+------------------------------------------------------------------+
//| 浮点数限制 |
//+------------------------------------------------------------------+
double ClampDouble(const double value, const double minValue, const double maxValue)
{
if(value < minValue)
return minValue;
if(value > maxValue)
return maxValue;
return value;
}
//+------------------------------------------------------------------+
+4
View File
@@ -55,6 +55,7 @@
| [Open Range Breakout-H-Max.mq5](Indicators/Open%20Range%20Breakout-H-Max.mq5) | 开盘区间突破(ORB)指标:绘制开盘区间高低/中点/价格目标(50%-500%),突破和回测标签,支持时区设置 | github | experimental | ✗ | ✗ | orb,breakout,openingrange |
| [SwingHighLow_and_Fibonacci.mq5](Indicators/SwingHighLow_and_Fibonacci.mq5) | 摆动高低点+斐波那契指标:识别摆动高/低点画箭头,摆动高点后接低点时绘制斐波那契回撤 | github | experimental | ✗ | ✗ | swing,fibonacci |
| [Timeframe Confluence Detector.mq5](Indicators/Timeframe%20Confluence%20Detector.mq5) | 多周期共振检测器:绘制A周期收盘价线及其与B/C周期的偏差线,偏移点数分离显示 | github | experimental | ✗ | ✗ | mtf,confluence,deviation |
| [SHK_Professional_Heikin_Ashi_Signals.mq5](Indicators/SHK_Professional_Heikin_Ashi_Signals.mq5) | Heikin Ashi信号指标:HA蜡烛颜色变化+MACD背离确认买卖箭头,动量转换圆圈+确认箭头,非重绘收盘K线逻辑,EA可读缓冲区 | self | experimental | ✗ | ✗ | heikinashi,signal,macd,divergence |
## Include
@@ -122,6 +123,9 @@
| [XAU_Quant_Reversion_m1_OLHC.mq5](Experts/90_Experimental/XAU_Quant_Reversion_m1_OLHC.mq5) | 全功能均值回归EA(OHLC版),Z-Score入场+保本/追踪止损/CSV交易记录/价格速度过滤/亏损冷却/ADX过滤/重力退出/波动率自适应阈值,最完善变体 | github | experimental | ✓ | ✗ | gold,reversion,zscore,full
| [XAU_Quant_Reversion_TickRobust.mq5](Experts/90_Experimental/XAU_Quant_Reversion_TickRobust.mq5) | 每根K线决策的均值回归EA,成本门控+服务器端限价TP+转向确认+宽止损,专为Every Tick回测设计 | github | experimental | ✓ | ✗ | gold,reversion,tickrobust,costgate
| [XAU_Quant_Reversion_IdealEA.mq5](Experts/90_Experimental/XAU_Quant_Reversion_IdealEA.mq5) | 均值回归Z-Score EA(IdealEA变体),基础版:Z-Score入场,H1趋势过滤,ATR追踪止损,无额外过滤器(简洁) | github | experimental | ✓ | ✗ | gold,reversion,zscore,basic
| [PriceAction_EngulfingZone_EA.mq5](Experts/90_Experimental/PriceAction_EngulfingZone_EA.mq5) | 纯价格行为EA:XAUUSD M5,摆动结构判趋势,吞没形态在支撑阻力区形成,等价格离区后回测再出现新吞没时入场,1:1风险收益,图表可视化区域 | self | experimental | ✓ | ✗ | gold,priceaction,engulfing,zone,retest
| [1h_eur_usd.mq5](Experts/90_Experimental/1h_eur_usd.mq5) | EURUSD 趋势突破EA:双均线(默认SMA10/SMA100)定方向,MACD(12,26,9)从负轴上穿/正轴下穿确认,突破近N根K线高低点入场,固定止损止盈+追踪止损 | mql5 | experimental | ✓ | ✗ | eur,macross,macd,breakout,trailing
| [改良海龟.mq5](Experts/90_Experimental/改良海龟.mq5) | 改良海龟策略:短周期唐奇安通道突破入场+长周期通道中轨趋势筛选,大周期趋势启动后收敛离场距离,持仓时间自适应收紧,风险比例动态计算手数 | self | experimental | ✓ | ✗ | turtle,donchian,breakout,trend,adaptive
---
@@ -0,0 +1,840 @@
//+------------------------------------------------------------------+
//| SHK_Professional_Heikin_Ashi_Signals.mq5 |
//| Original Heikin Ashi Signal Engine |
//| Pure HA engine + momentum-shift circles + confirmed arrows |
//| Non repaint closed bar logic | EA readable buffers |
//+------------------------------------------------------------------+
#property copyright "SHK"
#property link ""
#property version "1.02"
#property description "Professional Heikin Ashi candle engine with HA color change plus MACD divergence confirmed BUY/SELL arrows. Closed bar, EA integration friendly."
#property strict
#property indicator_chart_window
#property indicator_buffers 10
#property indicator_plots 5
//--- buffer index map (visible + calc)
// 0 = HA Open
// 1 = HA High
// 2 = HA Low
// 3 = HA Close
// 4 = HA Color Index
// 5 = Buy Momentum Circle
// 6 = Sell Momentum Circle
// 7 = Buy Confirmed Arrow
// 8 = Sell Confirmed Arrow
// 9 = Visual ATR (calculation only)
enum CandleColorMode
{
COLOR_STANDARD = 0,
COLOR_TREND_BASED = 1,
COLOR_MOMENTUM_BASED = 2
};
input int MaxBarsToCalculate = 3000; // 最大计算K线数
input bool ShowHeikinAshiCandles = true; // 显示Heikin Ashi蜡烛
input bool ShowOriginalCandlesFaint = false; // 显示原始蜡烛(淡色)
input bool EnableSignals = true; // 启用信号
input bool UseClosedBarsOnly = true; // 仅使用已收盘K线
input CandleColorMode CandleMode = COLOR_STANDARD; // 蜡烛颜色模式
input color BullColor = clrLime; // 多头颜色
input color BearColor = clrRed; // 空头颜色
input color StrongBullColor = clrDodgerBlue; // 强多头颜色
input color StrongBearColor = clrOrange; // 强空头颜色
input color NeutralColor = clrGray; // 中性颜色
input int ConfirmationCandles = 1; // 确认K线数(1-10)
input int SignalLookbackBars = 1000; // 信号回溯K线数
input double MinBodyPercent = 0.0; // 最小实体百分比
input bool SignalOnHAColorChange = true; // HA颜色变化即信号
input bool RequireMACDDivergenceConfirmation = true; // 需要MACD背离确认
input int MACDFastEMA = 12; // MACD快EMA
input int MACDSlowEMA = 26; // MACD慢EMA
input int MACDSignalEMA = 5; // MACD信号EMA
input int DivergencePivotLookback = 2; // 背离枢轴回溯
input int DivergenceSearchBars = 200; // 背离搜索K线数
input int DivergenceConfirmWindow = 30; // 背离确认窗口
input bool RequireStrongCandleForCircle = false; // 圆圈需强K线
input bool CancelSignalOnOppositeHA = true; // 反向HA取消信号
input bool ConfirmByCloseBreak = true; // 收盘突破确认
input double ArrowOffsetATR = 0.25; // 箭头偏移ATR倍数
input double CircleOffsetATR = 0.15; // 圆圈偏移ATR倍数
input int VisualATRPeriod = 14; // 可视ATR周期
input bool EnablePopupAlert = true; // 启用弹窗警报
input bool EnablePushAlert = false; // 启用推送警报
input bool EnableEmailAlert = false; // 启用邮件警报
input bool EnableSoundAlert = false; // 启用声音警报
input string BuySoundFile = "alert.wav"; // 买入声音文件
input string SellSoundFile = "alert2.wav"; // 卖出声音文件
input int AlertCooldownSeconds = 120; // 警报冷却秒数
input bool AlertOnlyRealtime = true; // 仅实时警报
double HAOpen[];
double HAHigh[];
double HALow[];
double HAClose[];
double HAColor[];
double BuyCircle[];
double SellCircle[];
double BuyArrow[];
double SellArrow[];
double ATRBuf[];
double MacdFastBuf[];
double MacdSlowBuf[];
double MacdSignalBuf[];
double MacdHistBuf[];
bool BullDivConfirmed[];
bool BearDivConfirmed[];
#define COL_BULL 0
#define COL_BEAR 1
#define COL_STRONG_BULL 2
#define COL_STRONG_BEAR 3
#define COL_NEUTRAL 4
#define MOM_BODY_WINDOW 14
#define STRONG_WICK_FRAC 0.10
#define CIRCLE_WICK_FRAC 0.15
#define ARROW_BUY_CODE 233
#define ARROW_SELL_CODE 234
#define CIRCLE_CODE 108
int g_firstBar = 1;
int g_confCandles = 3;
//--- pending momentum->confirmation state machine
bool g_buyPending = false;
double g_buyRefHigh = 0.0;
int g_buyBarsLeft = 0;
bool g_sellPending = false;
double g_sellRefLow = 0.0;
int g_sellBarsLeft = 0;
//--- signal processing cursor (by time, robust to index shifts)
datetime g_lastClosedTime = 0;
//--- alert dedupe
datetime g_lastAlertTime = 0;
int g_lastAlertDir = 0;
datetime g_lastAlertWall = 0;
bool IsBullishHA(const int i)
{
return(HAClose[i] > HAOpen[i]);
}
bool IsBearishHA(const int i)
{
return(HAClose[i] < HAOpen[i]);
}
double GetBodyPercent(const int i)
{
double range = HAHigh[i] - HALow[i];
if(range <= 0.0)
return(0.0);
return(MathAbs(HAClose[i] - HAOpen[i]) / range * 100.0);
}
double AvgHABody(const int i, const int win, const int firstBar)
{
int st = i - win;
if(st < firstBar)
return(0.0);
double s = 0.0;
int c = 0;
for(int k = st; k < i; k++)
{
s += MathAbs(HAClose[k] - HAOpen[k]);
c++;
}
return(c > 0 ? s / c : 0.0);
}
int GetCandleColorIndex(const int i)
{
double range = HAHigh[i] - HALow[i];
bool bull = IsBullishHA(i);
bool bear = IsBearishHA(i);
if(!bull && !bear)
return(COL_NEUTRAL);
if(CandleMode == COLOR_STANDARD)
return(bull ? COL_BULL : COL_BEAR);
if(CandleMode == COLOR_TREND_BASED)
{
if(range <= 0.0)
return(bull ? COL_BULL : COL_BEAR);
double lowerWick = MathMin(HAOpen[i], HAClose[i]) - HALow[i];
double upperWick = HAHigh[i] - MathMax(HAOpen[i], HAClose[i]);
if(bull)
return((lowerWick <= STRONG_WICK_FRAC * range) ? COL_STRONG_BULL : COL_BULL);
else
return((upperWick <= STRONG_WICK_FRAC * range) ? COL_STRONG_BEAR : COL_BEAR);
}
// COLOR_MOMENTUM_BASED
double body = MathAbs(HAClose[i] - HAOpen[i]);
double avgBody = AvgHABody(i, MOM_BODY_WINDOW, g_firstBar);
if(avgBody > 0.0 && body >= 1.2 * avgBody)
return(bull ? COL_STRONG_BULL : COL_STRONG_BEAR);
return(bull ? COL_BULL : COL_BEAR);
}
void ComputeHABar(const int i,
const double &open[], const double &high[],
const double &low[], const double &close[])
{
double haClose = (open[i] + high[i] + low[i] + close[i]) / 4.0;
double haOpen = (HAOpen[i - 1] + HAClose[i - 1]) / 2.0;
double haHigh = MathMax(high[i], MathMax(haOpen, haClose));
double haLow = MathMin(low[i], MathMin(haOpen, haClose));
HAOpen[i] = haOpen;
HAClose[i] = haClose;
HAHigh[i] = haHigh;
HALow[i] = haLow;
}
void InitHASeed(const int firstBar,
const double &open[], const double &high[],
const double &low[], const double &close[])
{
double haClose = (open[firstBar] + high[firstBar] + low[firstBar] + close[firstBar]) / 4.0;
double haOpen = (open[firstBar] + close[firstBar]) / 2.0;
HAClose[firstBar] = haClose;
HAOpen[firstBar] = haOpen;
HAHigh[firstBar] = MathMax(high[firstBar], MathMax(haOpen, haClose));
HALow[firstBar] = MathMin(low[firstBar], MathMin(haOpen, haClose));
}
double GetVisualATR(const int i,
const double &high[], const double &low[], const double &close[],
const int firstBar)
{
int p = VisualATRPeriod;
if(p < 1) p = 1;
int st = i - p + 1;
if(st < firstBar) st = firstBar;
if(st < 1) st = 1;
double sum = 0.0;
int n = 0;
for(int k = st; k <= i; k++)
{
double tr = high[k] - low[k];
double a = MathAbs(high[k] - close[k - 1]);
double b = MathAbs(low[k] - close[k - 1]);
if(a > tr) tr = a;
if(b > tr) tr = b;
sum += tr;
n++;
}
double atr = (n > 0) ? sum / n : (high[i] - low[i]);
ATRBuf[i] = atr;
return(atr);
}
bool IsMacdPeak(const int i, const int lookback, const int firstBar, const int lastBar)
{
if(i - lookback < firstBar || i + lookback > lastBar)
return(false);
for(int k = 1; k <= lookback; k++)
{
if(MacdHistBuf[i] <= MacdHistBuf[i - k] || MacdHistBuf[i] <= MacdHistBuf[i + k])
return(false);
}
return(true);
}
bool IsMacdTrough(const int i, const int lookback, const int firstBar, const int lastBar)
{
if(i - lookback < firstBar || i + lookback > lastBar)
return(false);
for(int k = 1; k <= lookback; k++)
{
if(MacdHistBuf[i] >= MacdHistBuf[i - k] || MacdHistBuf[i] >= MacdHistBuf[i + k])
return(false);
}
return(true);
}
void ComputeMACDDivergence(const int rates_total,
const int firstBar,
const int lastClosed,
const double &high[],
const double &low[],
const double &close[])
{
ArrayResize(MacdFastBuf, rates_total);
ArrayResize(MacdSlowBuf, rates_total);
ArrayResize(MacdSignalBuf, rates_total);
ArrayResize(MacdHistBuf, rates_total);
ArrayResize(BullDivConfirmed, rates_total);
ArrayResize(BearDivConfirmed, rates_total);
for(int i = 0; i < rates_total; i++)
{
MacdFastBuf[i] = 0.0;
MacdSlowBuf[i] = 0.0;
MacdSignalBuf[i] = 0.0;
MacdHistBuf[i] = 0.0;
BullDivConfirmed[i] = false;
BearDivConfirmed[i] = false;
}
if(!RequireMACDDivergenceConfirmation)
return;
int fast = MACDFastEMA;
int slow = MACDSlowEMA;
int signal = MACDSignalEMA;
int pivotLookback = DivergencePivotLookback;
int searchBars = DivergenceSearchBars;
if(fast < 1) fast = 1;
if(slow < 1) slow = 1;
if(signal < 1) signal = 1;
if(pivotLookback < 1) pivotLookback = 1;
if(searchBars < 1) searchBars = 1;
if(lastClosed < firstBar + pivotLookback * 2 + 2)
return;
double fastAlpha = 2.0 / ((double)fast + 1.0);
double slowAlpha = 2.0 / ((double)slow + 1.0);
double signalAlpha = 2.0 / ((double)signal + 1.0);
for(int i = firstBar; i <= lastClosed; i++)
{
double price = close[i];
if(i == firstBar)
{
MacdFastBuf[i] = price;
MacdSlowBuf[i] = price;
MacdSignalBuf[i] = 0.0;
MacdHistBuf[i] = 0.0;
continue;
}
MacdFastBuf[i] = fastAlpha * price + (1.0 - fastAlpha) * MacdFastBuf[i - 1];
MacdSlowBuf[i] = slowAlpha * price + (1.0 - slowAlpha) * MacdSlowBuf[i - 1];
double macdMain = MacdFastBuf[i] - MacdSlowBuf[i];
MacdSignalBuf[i] = signalAlpha * macdMain + (1.0 - signalAlpha) * MacdSignalBuf[i - 1];
MacdHistBuf[i] = macdMain - MacdSignalBuf[i];
}
int lastPivot = lastClosed - pivotLookback;
for(int i = firstBar + pivotLookback; i <= lastPivot; i++)
{
if(IsMacdTrough(i, pivotLookback, firstBar, lastClosed))
{
int prevTrough = -1;
int oldest = i - searchBars;
if(oldest < firstBar + pivotLookback)
oldest = firstBar + pivotLookback;
for(int j = i - 1; j >= oldest; j--)
{
if(IsMacdTrough(j, pivotLookback, firstBar, lastClosed))
{
prevTrough = j;
break;
}
}
if(prevTrough >= 0 && low[i] < low[prevTrough] && MacdHistBuf[i] > MacdHistBuf[prevTrough])
BullDivConfirmed[i + pivotLookback] = true;
}
if(IsMacdPeak(i, pivotLookback, firstBar, lastClosed))
{
int prevPeak = -1;
int oldest = i - searchBars;
if(oldest < firstBar + pivotLookback)
oldest = firstBar + pivotLookback;
for(int j = i - 1; j >= oldest; j--)
{
if(IsMacdPeak(j, pivotLookback, firstBar, lastClosed))
{
prevPeak = j;
break;
}
}
if(prevPeak >= 0 && high[i] > high[prevPeak] && MacdHistBuf[i] < MacdHistBuf[prevPeak])
BearDivConfirmed[i + pivotLookback] = true;
}
}
}
bool RecentMACDDivergenceConfirmed(const int i, const bool bullish)
{
if(!RequireMACDDivergenceConfirmation)
return(true);
int window = DivergenceConfirmWindow;
if(window < 0)
window = 0;
int first = i - window;
if(first < g_firstBar)
first = g_firstBar;
for(int k = i; k >= first; k--)
{
if(bullish && BullDivConfirmed[k])
return(true);
if(!bullish && BearDivConfirmed[k])
return(true);
}
return(false);
}
void ClearSignalBuffers(const int from, const int to)
{
for(int i = from; i <= to; i++)
{
BuyCircle[i] = EMPTY_VALUE;
SellCircle[i] = EMPTY_VALUE;
BuyArrow[i] = EMPTY_VALUE;
SellArrow[i] = EMPTY_VALUE;
}
}
void ClearAllBuffers(const int total)
{
for(int i = 0; i < total; i++)
{
HAOpen[i] = EMPTY_VALUE;
HAHigh[i] = EMPTY_VALUE;
HALow[i] = EMPTY_VALUE;
HAClose[i] = EMPTY_VALUE;
HAColor[i] = 0.0;
ATRBuf[i] = 0.0;
BuyCircle[i] = EMPTY_VALUE;
SellCircle[i] = EMPTY_VALUE;
BuyArrow[i] = EMPTY_VALUE;
SellArrow[i] = EMPTY_VALUE;
}
}
void ResetSignalState()
{
g_buyPending = false;
g_buyRefHigh = 0.0;
g_buyBarsLeft = 0;
g_sellPending = false;
g_sellRefLow = 0.0;
g_sellBarsLeft = 0;
}
void FireAlert(const int dir, const string typeText,
const double price, const datetime candleTime)
{
if(!(EnablePopupAlert || EnablePushAlert || EnableEmailAlert || EnableSoundAlert))
return;
// per-candle + direction dedupe
if(candleTime == g_lastAlertTime && dir == g_lastAlertDir)
return;
// cooldown on same direction
if(g_lastAlertWall > 0 && dir == g_lastAlertDir &&
(TimeCurrent() - g_lastAlertWall) < AlertCooldownSeconds)
return;
string tf = StringSubstr(EnumToString((ENUM_TIMEFRAMES)_Period), 7);
string msg = StringFormat("SHK Heikin Ashi Signals | %s %s | %s | Price: %s | Time: %s",
_Symbol, tf, typeText,
DoubleToString(price, _Digits),
TimeToString(candleTime, TIME_DATE | TIME_MINUTES));
if(EnablePopupAlert) Alert(msg);
if(EnablePushAlert) SendNotification(msg);
if(EnableEmailAlert) SendMail("SHK Heikin Ashi Signal", msg);
if(EnableSoundAlert) PlaySound(dir > 0 ? BuySoundFile : SellSoundFile);
g_lastAlertTime = candleTime;
g_lastAlertDir = dir;
g_lastAlertWall = TimeCurrent();
}
bool HasConfirmedHAColorChange(const int i, const bool bullish)
{
int candles = g_confCandles;
if(candles < 1)
candles = 1;
int runStart = i - candles + 1;
int previous = runStart - 1;
if(runStart < g_firstBar + 1 || previous < g_firstBar)
return(false);
for(int k = runStart; k <= i; k++)
{
if(bullish && !IsBullishHA(k))
return(false);
if(!bullish && !IsBearishHA(k))
return(false);
if(GetBodyPercent(k) < MinBodyPercent)
return(false);
}
if(bullish)
return(IsBearishHA(previous));
return(IsBullishHA(previous));
}
void ProcessColorChangeArrow(const int i, const datetime &time[], const double atr, const bool allowAlert)
{
if(HasConfirmedHAColorChange(i, true) && RecentMACDDivergenceConfirmed(i, true))
{
BuyArrow[i] = HALow[i] - ArrowOffsetATR * atr;
if(allowAlert)
FireAlert(+1, "STRONG BUY HA + MACD Divergence", HAClose[i], time[i]);
return;
}
if(HasConfirmedHAColorChange(i, false) && RecentMACDDivergenceConfirmed(i, false))
{
SellArrow[i] = HAHigh[i] + ArrowOffsetATR * atr;
if(allowAlert)
FireAlert(-1, "STRONG SELL HA + MACD Divergence", HAClose[i], time[i]);
}
}
void ProcessMomentumAndConfirmation(const int i, const datetime &time[], const bool allowAlert)
{
// fresh signal slots for this bar
BuyCircle[i] = EMPTY_VALUE;
SellCircle[i] = EMPTY_VALUE;
BuyArrow[i] = EMPTY_VALUE;
SellArrow[i] = EMPTY_VALUE;
if(i < g_firstBar + 1)
return;
double atr = (ATRBuf[i] != EMPTY_VALUE && ATRBuf[i] > 0.0)
? ATRBuf[i] : (HAHigh[i] - HALow[i]);
bool curBull = IsBullishHA(i);
bool curBear = IsBearishHA(i);
bool prevBull = IsBullishHA(i - 1);
bool prevBear = IsBearishHA(i - 1);
if(SignalOnHAColorChange)
{
ProcessColorChangeArrow(i, time, atr, allowAlert);
return;
}
bool placedBuyArrow = false;
bool placedSellArrow = false;
//--- 1) Resolve pending BUY confirmation against bar i -----------
if(g_buyPending)
{
if(CancelSignalOnOppositeHA && curBear)
{
g_buyPending = false;
}
else
{
bool brk = ConfirmByCloseBreak ? (HAClose[i] > g_buyRefHigh)
: (HAHigh[i] > g_buyRefHigh);
if(brk)
{
BuyArrow[i] = HALow[i] - ArrowOffsetATR * atr;
placedBuyArrow = true;
g_buyPending = false;
if(allowAlert)
FireAlert(+1, "BUY Confirmed", HAClose[i], time[i]);
}
else
{
g_buyBarsLeft--;
if(g_buyBarsLeft <= 0)
g_buyPending = false;
}
}
}
//--- 2) Resolve pending SELL confirmation against bar i ----------
if(g_sellPending)
{
if(CancelSignalOnOppositeHA && curBull)
{
g_sellPending = false;
}
else
{
bool brk = ConfirmByCloseBreak ? (HAClose[i] < g_sellRefLow)
: (HALow[i] < g_sellRefLow);
if(brk && !placedBuyArrow) // never both directions on the same candle
{
SellArrow[i] = HAHigh[i] + ArrowOffsetATR * atr;
placedSellArrow = true;
g_sellPending = false;
if(allowAlert)
FireAlert(-1, "SELL Confirmed", HAClose[i], time[i]);
}
else
{
g_sellBarsLeft--;
if(g_sellBarsLeft <= 0)
g_sellPending = false;
}
}
}
//--- 3) Detect a NEW momentum circle on bar i --------------------
double bodyPct = GetBodyPercent(i);
double avgBody = AvgHABody(i, MOM_BODY_WINDOW, g_firstBar);
double range = HAHigh[i] - HALow[i];
double body = MathAbs(HAClose[i] - HAOpen[i]);
// Bullish momentum shift: prev bearish -> current bullish
if(prevBear && curBull && !placedSellArrow)
{
bool ok = (bodyPct >= MinBodyPercent);
if(ok && RequireStrongCandleForCircle)
{
double lowerWick = MathMin(HAOpen[i], HAClose[i]) - HALow[i];
bool strong = (range > 0.0 && lowerWick <= CIRCLE_WICK_FRAC * range) ||
(avgBody > 0.0 && body >= avgBody);
ok = strong;
}
if(ok)
{
BuyCircle[i] = HALow[i] - CircleOffsetATR * atr;
g_buyPending = true;
g_buyRefHigh = HAHigh[i];
g_buyBarsLeft = g_confCandles;
if(allowAlert)
FireAlert(+1, "BUY Momentum", HAClose[i], time[i]);
}
}
// Bearish momentum shift: prev bullish -> current bearish
else if(prevBull && curBear && !placedBuyArrow)
{
bool ok = (bodyPct >= MinBodyPercent);
if(ok && RequireStrongCandleForCircle)
{
double upperWick = HAHigh[i] - MathMax(HAOpen[i], HAClose[i]);
bool strong = (range > 0.0 && upperWick <= CIRCLE_WICK_FRAC * range) ||
(avgBody > 0.0 && body >= avgBody);
ok = strong;
}
if(ok)
{
SellCircle[i] = HAHigh[i] + CircleOffsetATR * atr;
g_sellPending = true;
g_sellRefLow = HALow[i];
g_sellBarsLeft = g_confCandles;
if(allowAlert)
FireAlert(-1, "SELL Momentum", HAClose[i], time[i]);
}
}
}
int OnInit()
{
SetIndexBuffer(0, HAOpen, INDICATOR_DATA);
SetIndexBuffer(1, HAHigh, INDICATOR_DATA);
SetIndexBuffer(2, HALow, INDICATOR_DATA);
SetIndexBuffer(3, HAClose, INDICATOR_DATA);
SetIndexBuffer(4, HAColor, INDICATOR_COLOR_INDEX);
SetIndexBuffer(5, BuyCircle, INDICATOR_DATA);
SetIndexBuffer(6, SellCircle, INDICATOR_DATA);
SetIndexBuffer(7, BuyArrow, INDICATOR_DATA);
SetIndexBuffer(8, SellArrow, INDICATOR_DATA);
SetIndexBuffer(9, ATRBuf, INDICATOR_CALCULATIONS);
//--- Plot 0: Heikin Ashi color candles
PlotIndexSetInteger(0, PLOT_DRAW_TYPE, ShowHeikinAshiCandles ? DRAW_COLOR_CANDLES : DRAW_NONE);
PlotIndexSetInteger(0, PLOT_COLOR_INDEXES, 5);
PlotIndexSetInteger(0, PLOT_LINE_COLOR, COL_BULL, BullColor);
PlotIndexSetInteger(0, PLOT_LINE_COLOR, COL_BEAR, BearColor);
PlotIndexSetInteger(0, PLOT_LINE_COLOR, COL_STRONG_BULL, StrongBullColor);
PlotIndexSetInteger(0, PLOT_LINE_COLOR, COL_STRONG_BEAR, StrongBearColor);
PlotIndexSetInteger(0, PLOT_LINE_COLOR, COL_NEUTRAL, NeutralColor);
PlotIndexSetString(0, PLOT_LABEL, "HA Open;HA High;HA Low;HA Close");
PlotIndexSetDouble(0, PLOT_EMPTY_VALUE, EMPTY_VALUE);
//--- Plot 1: Buy Momentum Circle
PlotIndexSetInteger(1, PLOT_DRAW_TYPE, DRAW_ARROW);
PlotIndexSetInteger(1, PLOT_ARROW, CIRCLE_CODE);
PlotIndexSetInteger(1, PLOT_LINE_COLOR, StrongBullColor);
PlotIndexSetInteger(1, PLOT_LINE_WIDTH, 2);
PlotIndexSetString(1, PLOT_LABEL, "Buy Momentum Circle");
PlotIndexSetDouble(1, PLOT_EMPTY_VALUE, EMPTY_VALUE);
//--- Plot 2: Sell Momentum Circle
PlotIndexSetInteger(2, PLOT_DRAW_TYPE, DRAW_ARROW);
PlotIndexSetInteger(2, PLOT_ARROW, CIRCLE_CODE);
PlotIndexSetInteger(2, PLOT_LINE_COLOR, StrongBearColor);
PlotIndexSetInteger(2, PLOT_LINE_WIDTH, 2);
PlotIndexSetString(2, PLOT_LABEL, "Sell Momentum Circle");
PlotIndexSetDouble(2, PLOT_EMPTY_VALUE, EMPTY_VALUE);
//--- Plot 3: Buy Confirmed Arrow
PlotIndexSetInteger(3, PLOT_DRAW_TYPE, DRAW_ARROW);
PlotIndexSetInteger(3, PLOT_ARROW, ARROW_BUY_CODE);
PlotIndexSetInteger(3, PLOT_LINE_COLOR, BullColor);
PlotIndexSetInteger(3, PLOT_LINE_WIDTH, 3);
PlotIndexSetString(3, PLOT_LABEL, "Buy Confirmed Arrow");
PlotIndexSetDouble(3, PLOT_EMPTY_VALUE, EMPTY_VALUE);
//--- Plot 4: Sell Confirmed Arrow
PlotIndexSetInteger(4, PLOT_DRAW_TYPE, DRAW_ARROW);
PlotIndexSetInteger(4, PLOT_ARROW, ARROW_SELL_CODE);
PlotIndexSetInteger(4, PLOT_LINE_COLOR, BearColor);
PlotIndexSetInteger(4, PLOT_LINE_WIDTH, 3);
PlotIndexSetString(4, PLOT_LABEL, "Sell Confirmed Arrow");
PlotIndexSetDouble(4, PLOT_EMPTY_VALUE, EMPTY_VALUE);
//--- digits / name
IndicatorSetInteger(INDICATOR_DIGITS, _Digits);
IndicatorSetString(INDICATOR_SHORTNAME, "SHK Pro Heikin Ashi Signals");
//--- clamp confirmation candles 1..10
g_confCandles = ConfirmationCandles;
if(g_confCandles < 1) g_confCandles = 1;
if(g_confCandles > 10) g_confCandles = 10;
//--- original candle visibility
if(!ShowOriginalCandlesFaint)
{
ChartSetInteger(0, CHART_COLOR_CHART_UP, clrNONE);
ChartSetInteger(0, CHART_COLOR_CHART_DOWN, clrNONE);
ChartSetInteger(0, CHART_COLOR_CANDLE_BULL, clrNONE);
ChartSetInteger(0, CHART_COLOR_CANDLE_BEAR, clrNONE);
}
else
{
ChartSetInteger(0, CHART_COLOR_CHART_UP, clrDimGray);
ChartSetInteger(0, CHART_COLOR_CHART_DOWN, clrDimGray);
ChartSetInteger(0, CHART_COLOR_CANDLE_BULL, clrDimGray);
ChartSetInteger(0, CHART_COLOR_CANDLE_BEAR, clrDimGray);
}
ResetSignalState();
g_lastClosedTime = 0;
g_lastAlertTime = 0;
g_lastAlertDir = 0;
g_lastAlertWall = 0;
return(INIT_SUCCEEDED);
}
int OnCalculate(const int rates_total,
const int prev_calculated,
const datetime &time[],
const double &open[],
const double &high[],
const double &low[],
const double &close[],
const long &tick_volume[],
const long &volume[],
const int &spread[])
{
if(rates_total < 5)
return(0);
//--- non-series (chronological) indexing for everything
int maxBars = (MaxBarsToCalculate > 0) ? MaxBarsToCalculate : rates_total;
int firstBar = rates_total - maxBars;
if(firstBar < 1) firstBar = 1;
g_firstBar = firstBar;
int haStart;
if(prev_calculated == 0)
{
//--- full recalculation
ClearAllBuffers(rates_total);
ResetSignalState();
g_lastClosedTime = 0;
InitHASeed(firstBar, open, high, low, close);
GetVisualATR(firstBar, high, low, close, firstBar);
HAColor[firstBar] = (double)GetCandleColorIndex(firstBar);
haStart = firstBar + 1;
}
else
{
//--- incremental: recompute from last (possibly updated) bar
haStart = prev_calculated - 1;
if(haStart < firstBar + 1) haStart = firstBar + 1;
}
//--- HA engine + visual ATR + colors (includes forming bar for visualization)
for(int i = haStart; i < rates_total; i++)
{
ComputeHABar(i, open, high, low, close);
GetVisualATR(i, high, low, close, firstBar);
HAColor[i] = (double)GetCandleColorIndex(i);
}
if(!EnableSignals)
return(rates_total);
//--- last bar eligible for signals (closed-bar logic by default)
int lastClosed = (UseClosedBarsOnly ? rates_total - 2 : rates_total - 1);
if(lastClosed < firstBar + 1)
return(rates_total);
ComputeMACDDivergence(rates_total, firstBar, lastClosed, high, low, close);
//--- determine signal scan start
int sStart;
if(prev_calculated == 0)
{
sStart = firstBar + 1;
int lb = (rates_total - 1) - SignalLookbackBars;
if(lb > sStart) sStart = lb;
}
else
{
sStart = lastClosed;
while(sStart > firstBar + 1 && time[sStart - 1] > g_lastClosedTime)
sStart--;
}
//--- process new closed bars in chronological order (deterministic, non-repaint)
for(int i = sStart; i <= lastClosed; i++)
{
if(prev_calculated != 0 && time[i] <= g_lastClosedTime)
continue;
bool realtime = (prev_calculated != 0);
bool allowAlert = realtime && (i == lastClosed) &&
(!AlertOnlyRealtime || realtime);
ProcessMomentumAndConfirmation(i, time, allowAlert);
g_lastClosedTime = time[i];
}
//--- keep the forming bar's signal slots empty (no signal until close)
if(UseClosedBarsOnly && rates_total - 1 >= 0)
{
BuyCircle[rates_total - 1] = EMPTY_VALUE;
SellCircle[rates_total - 1] = EMPTY_VALUE;
BuyArrow[rates_total - 1] = EMPTY_VALUE;
SellArrow[rates_total - 1] = EMPTY_VALUE;
}
return(rates_total);
}
+151
View File
@@ -0,0 +1,151 @@
# 1h_eur_usd
| 元数据字段 | 内容 |
|---|---|
| **类型** | Expert Advisor (EA) |
| **家族** | 90_Experimental (趋势跟踪) |
| **来源** | mql5 (barabashkakvn 改编 MetaQuotes 官方示例) |
| **版本** | 1.000 |
| **状态** | experimental |
| **编译** | 0 errors, 0 warnings |
| **核心逻辑** | 双均线(默认SMA10/SMA100)定趋势方向,MACD(12,26,9)从轴下方拐头上行/从轴上方拐头下行确认,突破近N根K线最高/最低点入场,固定止损止盈+追踪止损 |
## 一句话简介
EURUSD 趋势跟踪 EA,来自 barabashkakvn 改编的 MetaQuotes 官方示例。核心信号链:双均线(SMA10 > SMA100 看涨,反看涨跌) + MACD 柱线在零轴附近拐头方向(负轴上行/正轴下行) + 价格突破近 N 根 K 线的最高/最低点。每根新 K 线开盘时评估信号并执行交易,内置固定止损止盈和追踪止损。适合 1H 时间框架,但代码未强制限制周期。
## Input Parameters
| 参数 | 默认值 | 说明 |
|------|--------|------|
| InpLots | 0.1 | 手数 |
| InpStopLoss | 150 | 止损(点数) |
| InpTakeProfit | 150 | 止盈(点数) |
| InpTrailingStop | 5 | 追踪止损(点数) |
| InpTrailingStep | 5 | 追踪步长(点数) |
| InpCount | 10 | 元素数量(用于 iHighest 和 iLowest 的区间) |
| Inp_MA_First_ma_period | 10 | 均线一:平均周期 |
| Inp_MA_First_ma_shift | 0 | 均线一:水平偏移 |
| Inp_MA_First_ma_method | MODE_SMA | 均线一:平滑方式 |
| Inp_MA_First_applied_price | PRICE_CLOSE | 均线一:价格类型 |
| Inp_MA_Second_ma_period | 100 | 均线二:平均周期 |
| Inp_MA_Second_ma_shift | 0 | 均线二:水平偏移 |
| Inp_MA_Second_ma_method | MODE_SMA | 均线二:平滑方式 |
| Inp_MA_Second_applied_price | PRICE_CLOSE | 均线二:价格类型 |
| Inp_MACD_fast_ema_period | 12 | MACD:快速均线周期 |
| Inp_MACD_slow_ema_period | 26 | MACD:慢速均线周期 |
| Inp_MACD_signal_period | 9 | MACD:信号线周期 |
| Inp_MACD_applied_price | PRICE_CLOSE | MACD:价格类型 |
| m_magic | 199397822 | 魔术号 |
## Dependencies
| 类型 | 库 |
|------|-----|
| 标准库 | `<Trade\PositionInfo.mqh>` (CPositionInfo) |
| 标准库 | `<Trade\Trade.mqh>` (CTrade) |
| 标准库 | `<Trade\SymbolInfo.mqh>` (CSymbolInfo) |
## Indicator Handles
| 句柄 | 指标 | 用途 |
|------|------|------|
| handle_iMA_First | iMA(m_symbol.Name(), Period(), Inp_MA_First_ma_period, ...) | 快均线(默认SMA10)与慢均线比较判趋势 |
| handle_iMA_Second | iMA(m_symbol.Name(), Period(), Inp_MA_Second_ma_period, ...) | 慢均线(默认SMA100)与快均线比较判趋势 |
| handle_iMACD | iMACD(m_symbol.Name(), Period(), 12, 26, 9, PRICE_CLOSE) | 获取 MAIN_LINE 判断 MACD 从轴上/下方拐头 |
## Entry Points
### OnInit()
1. 验证:TrailingStep != 0(否则 INIT_PARAMETERS_INCORRECT)
2. 初始化 CSymbolInfo 对象,绑定当前 Symbol
3. CheckVolumeValue 验证手数合法性
4. 初始化 CTrade:MagicNumber、MarginMode、自动填充模式、滑点
5. 根据 Digits 调整 m_adjusted_point(3/5位价格 ×10)
6. 将 InpStopLoss/TakeProfit/TrailingStop/TrailingStep 转换为价格差值
7. 创建 iMA(快)、iMA(慢)、iMACD 指标句柄,失败则 INIT_FAILED
8. 返回 INIT_SUCCEEDED
### OnTick()
1. **每次 tick 先执行 Trailing()** 追踪止损
2. 仅在新 K 线开盘时处理(基于 bar 0 时间戳判断)
3. 获取 MACD MAIN_LINE 近 5 个值
4. 获取 MA 当前值(ma_first_0, ma_second_0)
5. 获取近 InpCount 根 K 线的最高(top)和最低(bottom)
6. CopyRates 获取近 3 根 K 线数据
7. **买入判断**:
- 条件A:MACD[1] > MACD[2] 且 MACD[2] < MACD[3](MACD 从下方拐头上行) 且 MA快 > MA慢 且 Ask > rates[1].high 且 MACD[2] < 0
- 条件B:MACD[2] > MACD[3] 且 MACD[3] < MACD[4] 且 MA快 > MA慢 且 Ask > rates[2].high 且 MACD[3] < 0
- 满足任一 → 计算 SL/TP,调用 OpenBuy
8. **卖出判断**:
- 条件A:MACD[1] < MACD[2] 且 MACD[2] > MACD[3](MACD 从上方拐头下行) 且 MA快 < MA慢 且 Bid < rates[1].low 且 MACD[2] > 0
- 条件B:MACD[2] < MACD[3] 且 MACD[3] > MACD[4] 且 MA快 < MA慢 且 Bid < rates[2].low 且 MACD[3] > 0
- 满足任一 → 计算 SL/TP,调用 OpenSell
### OnDeinit()
空实现(不删除指标句柄,但 MT5 会在 EA 卸载时自动释放)
### OnTradeTransaction()
空实现(仅占位,未使用)
## Signal Logic
### 买入条件(任一满足)
1. MA快 > MA慢(趋势向上)
2. MACD 柱线从零轴下方拐头上行(连续两根 MACD 递增,且基准值 < 0)
3. 当前 Ask 突破近 InpCount 根 K 线的最高价(条件A 用 rates[1].high,条件B 用 rates[2].high)
### 卖出条件(任一满足)
1. MA快 < MA慢(趋势向下)
2. MACD 柱线从零轴上方拐头下行(连续两根 MACD 递减,且基准值 > 0)
3. 当前 Bid 跌破近 InpCount 根 K 线的最低价
## Risk Management
| 规则 | 描述 |
|------|------|
| 止损 | 固定点数(InpStopLoss),可设0关闭 |
| 止盈 | 固定点数(InpTakeProfit),可设0关闭 |
| 追踪止损 | 盈利超过 TrailingStop + TrailingStep 后启用,每 tick 更新 |
| 止停保护 | Buy 时检查 SL 不超过 Bid,Sell 时检查 SL 不超过 Ask,否则放弃开仓 |
| 填充模式 | 自动按品种 SetTypeFillingBySymbol |
| 滑点 | 10 点(硬编码 m_slippage) |
## Filters
| 过滤器 | 条件 |
|--------|------|
| 新K线 | 仅在新 K 线开盘时评估信号 |
| 趋势方向 | MA快 vs MA慢(看涨/看跌) |
| MACD 位置 | MACD 基准值必须在零轴下方(买入)或上方(卖出) |
| MACD 拐头 | 需连续两根 MACD 同向变化 |
| 价格突破 | Ask/Bid 突破近 InpCount 根 K 线 high/low |
| SL 有效性 | SL 不能与 Bid/Ask 反转 |
## Extractable Components
| 组件 | 优先级 | 目标目录 | 说明 |
|------|--------|---------|------|
| RefreshRates | 低 | Include/Common/ | 标准报价刷新保护 |
| CheckVolumeValue | 低 | Include/Common/ | 手数校验工具 |
| iMAGet / iMACDGetArray | 低 | Include/Common/ | 指标缓冲区读取封装 |
| Trailing | 中 | Include/RiskManagement/ | 追踪止损实现 |
| PrintResultTrade / PrintResultModify | 低 | Include/Common/ | 交易结果日志 |
## Known Issues
| 问题 | 严重程度 | 说明 |
|------|---------|------|
| OnDeinit 未释放句柄 | 低 | handle_iMA_First/Second/handle_iMACD 未调用 IndicatorRelease,MT5 会自动清理但非最佳实践 |
| OnTradeTransaction 空实现 | 低 | 占位函数,可删除 |
| 无持仓限制 | 中 | 每根新 K 线都可能开新仓,无同向持仓检查,可能导致多笔同向持仓 |
| 无品种/周期限制 | 中 | 文件名暗示 EURUSD 1H,但代码无验证,可挂任何图表 |
| 滑点硬编码 | 低 | m_slippage=10 非 input 参数,不可调节 |
| MACD 双条件冗余 | 低 | 条件A 和条件B 使用不同 shift(1和2),在 MACD 拐点附近可能同时满足,但只开一单(逻辑上无问题,只是冗余) |
## Notes
- 此 EA 为 MetaQuotes 官方 MQL5 教程示例的改编版本(barabashkakvn),主要用于教学
- 默认参数针对 EURUSD 1H 设计,在其他品种/周期上需自行调整均线周期和止损止盈
- 信号评估在新 K 线开盘那一刻执行,此时 MACD 和 MA 的值对应已收盘 K 线(因 CopyRates 获取 rates[1]/rates[2])
- Trailing() 每 tick 执行,不依赖新 K 线判断
@@ -0,0 +1,145 @@
# PriceAction_EngulfingZone_EA
| 元数据字段 | 内容 |
|---|---|
| **类型** | Expert Advisor (EA) |
| **家族** | 90_Experimental (价格行为) |
| **来源** | self (自研) |
| **版本** | 1.00 |
| **状态** | experimental |
| **编译** | 0 errors, 0 warnings |
| **核心逻辑** | 纯价格行为,XAUUSD M5,摆动点结构判趋势,吞没形态在支撑阻力区形成,等价格离区后回测再出现新吞没时入场,1:1风险收益 |
## 一句话简介
XAUUSD M5 纯价格行为 EA,完全不使用技术指标。通过摆动点(高低点)结构判断趋势方向,在摆动点附近识别看涨/看跌吞没形态后创建支撑/阻力区域。仅第一次吞没不交易、只建区,等待价格离开区域后再次回测,若回测过程中在该区域内形成**新的**吞没形态才触发交易。止损设在区域外(可配置缓冲区),止盈为1:1风险收益。支持图表可视化区域、点差过滤、区域失效机制。
## Input Parameters
| 参数 | 默认值 | 说明 |
|------|--------|------|
| InpManualLotSize | 0.0 | 手动手数(0=自动) |
| InpLotPer1000 | 0.04 | 每$1,000余额的手数(自动模式) |
| InpMaxLotSize | 2.0 | 最大手数 |
| InpMinLotSize | 0.01 | 最小手数 |
| InpSLBufferPoints | 50 | 止损缓冲区(超出区域外的点数) |
| InpSwingLookback | 3 | 摆动点回看K线数(每侧) |
| InpTrendSwingCount | 4 | 趋势判断所需摆动点数(最少3) |
| InpZoneExitCandles | 3 | 价格离开区域后回测所需最少K线数 |
| InpInvalidateZone | true | 价格反向穿越时使区域失效 |
| InpMagicNumber | 202507 | 魔术号 |
| InpMaxSlippage | 30 | 最大滑点(点数) |
| InpMaxSpreadPoints | 50 | 最大点差(点数) |
| InpShowZones | true | 在图表上绘制区域矩形 |
| InpBullZoneColor | clrDodgerBlue | 看涨区域颜色 |
| InpBearZoneColor | clrOrangeRed | 看跌区域颜色 |
## Dependencies
| 类型 | 库 |
|------|-----|
| 标准库 | `<Trade\Trade.mqh>` (CTrade) |
## Indicator Handles
无。本 EA 完全基于裸价格数据(iOpen/iHigh/iLow/iClose/iTime),不创建任何指标句柄。
## Entry Points
### OnInit()
1. 验证品种含 XAU/XAUUSD/GOLD,否则 INIT_FAILED
2. 验证周期为 M5,否则 INIT_FAILED
3. 验证参数(InpSwingLookback >= 1, InpTrendSwingCount >= 3)
4. 初始化 CTrade:MagicNumber、滑点、自动检测填充模式(FOK > IOC > RETURN)
5. 重置看涨/看跌区域为不活跃状态
6. 打印初始化信息
### OnTick()
1. **仅在新K线时处理**(基于 bar 0 的时间判断)
2. 优先执行上一K线的待处理信号(如果有,Buy 优先于 Sell)
3. 清除所有待处理信号
4. 点差检查:超过 InpMaxSpreadPoints 则跳过信号检测,仅管理区域
5. 管理区域(检查失效/离开状态)
6. 获取趋势方向
7. **看涨逻辑**:趋势 UP + 看涨吞没 + 在摆动低点附近 → 已有区域则在区域内形成新吞没时设置待处理 Buy 信号;无区域则创建区域
8. **看跌逻辑**:趋势 DOWN + 看跌吞没 + 在摆动高点附近 → 已有区域则在区域内形成新吞没时设置待处理 Sell 信号;无区域则创建区域
### OnDeinit()
1. 删除所有 EA 创建的图表对象
2. 打印卸载信息
## Signal Logic
### 买入信号链
1. 趋势检测:HH + HL 结构 → TREND_UP
2. 在摆动低点附近(shift=1)出现看涨吞没形态
3. 已有活跃看涨区域(第一次吞没只建区不交易)
4. 价格已离开区域(连续 >= InpZoneExitCandles 根K线收盘价高于 ZoneHigh)
5. 该区域尚未交易
6. 新吞没K线的完整实体在区域范围内(ZoneLow <= candleLow 且 candleHigh <= ZoneHigh)
7. 满足以上 → 设置 SignalPending,下一个K线开盘时执行 Buy
8. 执行时:SL = ZoneLow - SLBufferPoints×Point, TP = Entry + (Entry - SL)(1:1),按计算手数
### 卖出信号链
1. 趋势检测:LL + LH 结构 → TREND_DOWN
2. 在摆动高点附近(shift=1)出现看跌吞没形态
3. 已有活跃看跌区域
4. 价格已离开区域(连续 >= InpZoneExitCandles 根K线收盘价低于 ZoneLow)
5. 该区域尚未交易
6. 新吞没K线的完整实体在区域范围内
7. 满足以上 → 设置 SignalPending,下一个K线开盘时执行 Sell
8. 执行时:SL = ZoneHigh + SLBufferPoints×Point, TP = Entry - (SL - Entry)(1:1),按计算手数
## Risk Management
| 规则 | 描述 |
|------|------|
| 止损 | 区域外 + 可配置缓冲区点数,防止假突破 |
| 止盈 | 1:1 固定风险收益比 |
| 区域失效 | 价格反向关闭穿越区域时使区域失效(可配置关闭) |
| 点差过滤 | 超过最大点差时不开新信号 |
| 滑点控制 | CTrade SetDeviationInPoints |
| 填充模式 | 自动检测 FOK > IOC > RETURN |
| 持仓限制 | 同一时刻仅允许一个持仓(同 Magic) |
| 手数 | 可手设或按余额自动计算,受最小/最大手数及经纪商步长限制 |
| 止停距离 | 检查 SYMBOL_TRADE_STOPS_LEVEL 最小止停距离 |
## Filters
| 过滤器 | 阈值/条件 |
|--------|----------|
| 品种限制 | 仅限 XAUUSD/XAU/GOLD |
| 周期限制 | 仅限 M5 |
| 趋势方向 | 必须为 TREND_UP 或 TREND_DOWN 才交易 |
| 摆动点确认 | 吞没形态必须在摆动点附近 |
| 区域状态 | 必须已有活跃区域且价格已离开 |
| 点差 | 必须 <= InpMaxSpreadPoints |
| 最小止停 | 必须 >= SYMBOL_TRADE_STOPS_LEVEL×Point |
## Extractable Components
| 组件 | 优先级 | 目标目录 | 说明 |
|------|--------|---------|------|
| 摆动点检测(DetectSwingHigh/Low,IsAtSwingHigh/Low) | 高 | Include/Signals/ | 通用摆动点工具函数 |
| 吞没形态检测(IsBullishEngulfing/IsBearishEngulfing) | 高 | Include/Signals/ | 通用吞没形态识别 |
| 趋势检测(GetTrend) | 中 | Include/Signals/ | 基于摆动点的趋势判断 |
| 区域管理(EngulfingZone结构及生命周期) | 中 | Include/Signals/ | 供需区域创建/管理/失效 |
| 图表区域绘制(DrawZoneRectangle 等) | 低 | Include/Common/ | 可视化辅助 |
## Known Issues
| 问题 | 严重程度 | 说明 |
|------|---------|------|
| 仅支持 XAUUSD | 中 | OnInit 硬编码验证品种,不能直接用于其他品种 |
| 仅支持 M5 | 中 | OnInit 硬编码验证周期 |
| 单次区域 | 低 | 同时只保留一个看涨区域和一个看跌区域,旧区域被新区域覆盖 |
| 区域对象未清理 | 低 | InvalidateZone 仅改颜色未删除矩形对象;RemoveAllZoneObjects 仅在 EA 卸载时清理 |
| 区域矩形未随时间自动移除 | 低 | 即使区域已失效,矩形仍保留在图上直到 EA 卸载 |
| 无 TP 滑点保护 | 低 | TP 为市价触发,无挂单保护 |
## Notes
- 本 EA 设计精巧:第一次吞没只建区不交易,等价格离开后回测再形成新吞没才入场,避免了追单
- 所有信号分析在已收盘 K 线(shift >= 1)上进行,不重绘
- 摆动点检测严格避免使用未完成的 bar 0
- 区域可视化在图表上动态更新颜色反映区域状态(活跃/已离/已交易/已失效)
@@ -0,0 +1,153 @@
# 改良海龟
| 元数据字段 | 内容 |
|---|---|
| **类型** | Expert Advisor (EA) |
| **家族** | 90_Experimental (趋势跟踪) |
| **来源** | self (自研,comment header 注明 OpenAI) |
| **版本** | 1.00 |
| **状态** | experimental |
| **编译** | 0 errors, 0 warnings |
| **核心逻辑** | 30分钟唐奇安通道突破入场 + 长/短周期通道中轨趋势筛选 + 大周期趋势启动后收敛离场距离 + 持仓时间自适应收紧 + 风险比例动态计算手数 |
## 一句话简介
改良版海龟突破 EA。以短周期(默认25)唐奇安通道上下轨作为入场信号,价格收盘突破上轨做多、突破下轨做空,但仅当长周期(默认65)唐奇安通道和短周期通道中轨方向一致时才放行(TrendFilter_DoubleFilter)。持仓管理采用动态吊灯止损:按当前价格千分比计算离场幅度,大周期趋势启动时乘入收敛系数收紧,持仓时间越久系数越低,最终离场。支持风险比例自动计算手数(基于虚拟止损距离和 tick value),按保证金二次调整,确保不超仓位。
## Input Parameters
| 参数 | 默认值 | 说明 |
|------|--------|------|
| InpSignalTimeframe | PERIOD_M30 | 策略运行周期,推荐30分钟 |
| InpMagic | 2026080630 | 魔术编号 |
| InpDeviationPoints | 20 | 允许滑点,单位:点 |
| InpPrintLog | true | 是否打印交易日志 |
| InpBoLength | 25 | 短周期唐奇安通道参数 |
| InpFsLength | 65 | 长周期唐奇安通道参数 |
| InpTrendFilterMode | TrendFilter_DoubleFilter | 趋势筛选模式(0=仅短周期中轨,1=仅长周期中轨,2=双重过滤) |
| InpTRS | 15.0 | 基础动态离场幅度,按千分比计算 |
| InpXX | 0.40 | 大周期趋势启动后的离场收敛系数 |
| InpUseTimeTighten | true | 是否启用持仓时长自适应收紧 |
| InpTightenStartBars | 10 | 持仓多少根K线后开始收紧 |
| InpTightenFullBars | 80 | 持仓多少根K线后收紧至最低系数 |
| InpMinTightenCoef | 0.30 | 持仓时间收紧最低系数 |
| InpExitAnchorMode | ExitAnchor_StandardChandelier | 动态离场极值模式(0=标准吊灯,1=严格文字版) |
| InpUseRiskLot | true | 是否按风险比例自动计算手数 |
| InpRiskPercent | 1.00 | 单笔理论风险占账户余额比例 |
| InpFixedLots | 1.00 | 固定手数 |
| InpMinimumLots | 1.00 | 策略最低交易手数 |
| InpMaxMarginPercent | 80.0 | 最大可用保证金占用比例 |
| InpAllowBuy | true | 允许做多 |
| InpAllowSell | true | 允许做空 |
## Dependencies
| 类型 | 库 |
|------|-----|
| 标准库 | `<Trade/Trade.mqh>` (CTrade) |
## Indicator Handles
无。所有唐奇安通道计算通过 `CopyClose` + 手动遍历实现,不创建指标句柄。
## Entry Points
### OnInit()
1. 参数校验:InpBoLength >= 2,InpFsLength > InpBoLength,InpTRS > 0,InpXX ∈ (0,1],InpRiskPercent > 0(如启用风险手数),InpFixedLots > 0,InpMinimumLots > 0
2. 初始化 CTrade:MagicNumber、滑点、按品种自动填充模式
3. SyncCurrentSignalBar() 同步当前K线时间,避免EA加载瞬间追单
4. 打印初始化日志
5. 返回 INIT_SUCCEEDED
### OnTick()
1. `IsTradeEnvironmentReady()`: 检查终端交易权限、MQL交易权限、品种交易模式
2. `ManageOpenPosition()`: 如有持仓则执行动态离场逻辑,如平仓则直接返回
3. `IsNewSignalBar()`: 仅在新信号周期K线(默认M30)开盘时评估入场信号
4. `ProcessEntrySignal()`: 无持仓时评估唐奇安突破 + 趋势筛选 → 开仓
### OnDeinit()
1. 打印停止日志
## Signal Logic
### 入场条件(BUY)
1. 当前EA无同品种同Magic持仓
2. 品种在信号周期上有足够的K线(至少 `MathMax(InpBoLength, InpFsLength) + 5` 根)
3. 短周期唐奇安(默认25)上轨突破:上一根K线收盘价 > `boUpper`(shift=2的25周期收盘价最高)
4. 趋势筛选通过(`TrendPass`):
- **仅短周期中轨**:上一K线收盘 > 短周期通道中轨,且短周期中轨 >= 前一根中轨(中轨上升)
- **仅长周期中轨**:上一K线收盘 > 长周期通道中轨,且长周期中轨 >= 前一根中轨
- **双重过滤**:上述两个条件同时满足
5. 保证金和手数检查通过(CalculateTradeLots 返回 > 0)
### 入场条件(SELL)
对称:短周期唐奇安下轨突破 + 趋势筛选(中轨下降方向)
### 出场条件
每 tick 检查:
1. 获取持仓以来最高价和最低价(`GetExtremesSinceOpen`)
2. 计算离场距离:`baseDistance = currentPrice × InpTRS / 1000`
3. 时间收紧系数(`GetTimeTightenCoefficient`):
- 持仓 ≤ InpTightenStartBars(10):系数=1.0
- 持仓 ≥ InpTightenFullBars(80):系数=InpMinTightenCoef(0.30)
- 中间线性插值
4. 趋势收敛系数:大周期趋势启动(价格突破长周期通道上下轨)时系数=InpXX(0.40),否则=1.0
5. 最终离场距离 = `baseDistance × timeCoef × trendCoef`
6. 离场线计算:
- **标准吊灯(默认)**:多头 exitLine = 持仓以来最高价 - 距离;空头 exitLine = 持仓以来最低价 + 距离
- **严格文字版**:多头 exitLine = 持仓以来最低价 - 距离;空头 exitLine = 持仓以来最高价 + 距离
7. 多头当前 Bid <= exitLine 或 空头当前 Ask >= exitLine → 平仓
## Risk Management
| 规则 | 描述 |
|------|------|
| 动态止损 | 吊灯式止损,离场幅度按当前价格千分比计算,自适应收紧 |
| 趋势收敛 | 大周期趋势启动后离场距离乘以 InpXX(0.40),快速锁定利润 |
| 时间收紧 | 持仓越久止损越紧,最终降至 InpMinTightenCoef(0.30)系数 |
| 风险手数 | 基于虚拟止损距离 × tick value 计算每手亏损,按余额风险比例倒推手数 |
| 保证金保护 | 最终手数按 OrderCalcMargin 逐档递减,确保不超过最大保证金比例 |
| 最低手数 | 手数不得低于 InpMinimumLots,否则不开仓 |
| 滑点 | InpDeviationPoints(20点) |
| 单方向持仓 | 同一时刻仅允许一个方向的持仓 |
## Filters
| 过滤器 | 条件 |
|--------|------|
| 交易环境 | 终端/MQL交易权限,品种未禁用 |
| 新信号K线 | 仅在信号周期(默认M30)新K线开盘时评估入场 |
| 无持仓 | 有同品种同Magic持仓时不开新仓 |
| 足够数据 | 品种K线数 >= MathMax(InpBoLength, InpFsLength) + 5 |
| 通道突破 | 收盘价必须突破短周期唐奇安上下轨 |
| 趋势筛选 | 短/长周期通道中轨方向确认 |
| 保证金 | 手数不超过保证金限制 |
| 做多/做空开关 | InpAllowBuy/InpAllowSell |
## Extractable Components
| 组件 | 优先级 | 目标目录 | 说明 |
|------|--------|---------|------|
| 唐奇安通道(GetCloseDonchian) | 高 | Include/Signals/ | 通用收盘价唐奇安计算 |
| 趋势筛选(TrendPass) | 中 | Include/Signals/ | 通道中轨趋势确认逻辑 |
| 动态离场(ManageOpenPosition + CalculateExitDistance) | 中 | Include/RiskManagement/ | 吊灯止损+时间/趋势自适应 |
| 风险手数计算(CalculateTradeLots + AdjustLotsByMargin) | 中 | Include/MoneyManagement/ | 风险比例+保证金调整 |
| 工具函数(NormalizeLots/ClampDouble/VolumeDigits) | 低 | Include/Common/ | 通用数学工具 |
## Known Issues
| 问题 | 严重程度 | 说明 |
|------|---------|------|
| 入场不设止损止盈 | 中 | trade.Buy/Sell 传入 SL=0, TP=0,完全依赖动态离场,突发行情可能深套 |
| 不锁仓 | 低 | 同时只能一个方向持仓,适合单策略但无法对冲 |
| 保证金递减循环最多10000次 | 低 | 极端情况下可能性能受影响,但实际循环次数远小于此 |
| 入场不查点差 | 低 | 突破信号时若点差过大可能滑点严重 |
| 标准库路径 | 低 | `#include <Trade/Trade.mqh>` 使用正斜杠(非反斜杠),MT5兼容但与其他文件风格不一致 |
## Notes
- 文件名保留中文原名,不重命名
- 所有 input 参数注释已是中文,无需翻译
- 本策略无马丁格尔、无网格,每次只开一单,风险可控
- 适合趋势明显的品种(黄金/外汇主要货币对)
- 唐奇安通道基于收盘价(非最高价),信号略滞后但更稳健
@@ -0,0 +1,110 @@
# SHK_Professional_Heikin_Ashi_Signals.mq5
## Overview
- **Type**: Indicator (OnCalculate)
- **家族**: Heikin Ashi 信号引擎
- **来源**: self (SHK版权)
- **版本**: 1.02
- **状态**: experimental
- **核心逻辑**: 纯Heikin Ashi蜡烛引擎 + 颜色变化信号 + MACD背离确认的BUY/SELL箭头,基于已收盘K线逻辑,非重绘,提供EA可读缓冲区
- **Compile**: 通过(0 error, 0 warning)
## 一句话简介
专业级别的Heikin Ashi信号指标,在图上绘制HA蜡烛(可选标准/趋势/动量三种着色模式),并在HA颜色变化时结合MACD背离确认产生买卖箭头信号。支持动量转换圆圈(Circle)预信号 + 确认箭头(Arrow)两阶段状态机,仅使用已收盘K线,非重绘,稳定可靠。内置弹窗/推送/邮件/声音警报,缓冲区设计便于EA直接读取信号。
## Input Parameters
| 参数 | 默认值 | 说明 |
|------|--------|------|
| MaxBarsToCalculate | 3000 | 最大计算K线数 |
| ShowHeikinAshiCandles | true | 显示Heikin Ashi蜡烛 |
| ShowOriginalCandlesFaint | false | 显示原始蜡烛(淡色) |
| EnableSignals | true | 启用信号 |
| UseClosedBarsOnly | true | 仅使用已收盘K线 |
| CandleMode | COLOR_STANDARD | 蜡烛颜色模式: 0标准/1趋势/2动量 |
| BullColor | clrLime | 多头颜色 |
| BearColor | clrRed | 空头颜色 |
| StrongBullColor | clrDodgerBlue | 强多头颜色 |
| StrongBearColor | clrOrange | 强空头颜色 |
| NeutralColor | clrGray | 中性颜色 |
| ConfirmationCandles | 1 | 确认K线数(1-10) |
| SignalLookbackBars | 1000 | 信号回溯K线数 |
| MinBodyPercent | 0.0 | 最小实体百分比 |
| SignalOnHAColorChange | true | HA颜色变化即信号 |
| RequireMACDDivergenceConfirmation | true | 需要MACD背离确认 |
| MACDFastEMA | 12 | MACD快EMA |
| MACDSlowEMA | 26 | MACD慢EMA |
| MACDSignalEMA | 5 | MACD信号EMA |
| DivergencePivotLookback | 2 | 背离枢轴回溯 |
| DivergenceSearchBars | 200 | 背离搜索K线数 |
| DivergenceConfirmWindow | 30 | 背离确认窗口 |
| RequireStrongCandleForCircle | false | 圆圈需强K线 |
| CancelSignalOnOppositeHA | true | 反向HA取消信号 |
| ConfirmByCloseBreak | true | 收盘突破确认 |
| ArrowOffsetATR | 0.25 | 箭头偏移ATR倍数 |
| CircleOffsetATR | 0.15 | 圆圈偏移ATR倍数 |
| VisualATRPeriod | 14 | 可视ATR周期 |
| EnablePopupAlert | true | 启用弹窗警报 |
| EnablePushAlert | false | 启用推送警报 |
| EnableEmailAlert | false | 启用邮件警报 |
| EnableSoundAlert | false | 启用声音警报 |
| BuySoundFile | "alert.wav" | 买入声音文件 |
| SellSoundFile | "alert2.wav" | 卖出声音文件 |
| AlertCooldownSeconds | 120 | 警报冷却秒数 |
| AlertOnlyRealtime | true | 仅实时警报 |
## Dependencies
- **标准库**: 无(未使用 #include,纯SDK内置函数)
- **自定义库**: 无
## Indicator Handles
- **内置自定义指标计算**(非句柄,手动实现):
- 可视ATR(VisualATRPeriod,默认14)——手动TR累加均值
- MACD(12,26,5)——手动EMA双均线差分 + 信号线,柱值用于背离检测
## Entry Points
- **OnInit**: 绑定10个缓冲区(buffer 0-8为数据,9为ATR计算),配置5个Plot(HA彩色蜡烛/买圆圈/卖圆圈/买箭头/卖箭头),设置颜色与空值,钳制确认K线数(1-10),控制原始蜡烛可见性,重置信号状态与警报去重。
- **OnCalculate**: 计算HA引擎(seed + 逐bar递推),可视ATR,颜色索引;之后计算MACD背离数组;按时间游标顺序处理新增已收盘K线的动量转换与确认信号;保持形成中K线的信号槽为空;返回 rates_total。
## Signal Logic
- **两阶段状态机**(动量转换 → 确认):
1. **动量圆圈(Circle)**: 前bar为空头/当前转多头(或反向)时,满足最小实体百分比(可选强K线要求)则画圆圈,记录参考高低点并进入pending状态。
2. **确认箭头(Arrow)**: pending状态下,后续K线收盘价(或最高/最低价)突破参考位则确认,画箭头;若出现反向HA则取消;超过确认K线数仍未突破则超时失效。
- **HA颜色变化信号**: 若启用 SignalOnHAColorChange,当连续 ConfirmationCandles 根K线同向HA且前一根反向,结合近期MACD背离确认,直接画箭头。
- **MACD背离确认**: 检测HA动量枢轴(Peak/Trough)与价格枢轴的方向分歧(价格创新高但MACD柱未创新高=看跌背离;反之看涨),并在确认窗口内引用。
## Risk Management
- 无风险参数(纯信号指标,不交易)。
- 信号非重绘: 仅基于已收盘K线,prev_calculated 增量计算,使用时间游标避免索引位移。
## Filters
- MinBodyPercent: 信号K线最小实体百分比阈值
- RequireStrongCandleForCircle: 圆圈需强K线(下/上影线占比或相对平均实体)
- CancelSignalOnOppositeHA: 反向HA取消pending信号
- ConfirmByCloseBreak: 收盘价突破(而非高低价)确认
- RequireMACDDivergenceConfirmation: 是否要求MACD背离确认
- UseClosedBarsOnly: 仅对已收盘K线出信号
## Extractable Components
- 无(单文件Indicator,无独立mqh组件)。
## Known Issues
- MACD和历史背离计算仅在 EnableSignals 时执行,且用固定缓冲区(ArrayResize到 rates_total),未用标准 MACD 指标句柄,可能与MT5标准MACD数值有微小差异(手写EMA)。
- 批量重算时 GetVisualATR 从 firstBar 起算,若 FirstBar 处索引 -1 访问 close[k-1] 越界风险(已由 st<1 保护)。
## Notes
- 此文件为自研指标,命名符合规范。
- 缓冲区布局: 0=HA Open, 1=HA High, 2=HA Low, 3=HA Close, 4=HA Color Index, 5=Buy Circle, 6=Sell Circle, 7=Buy Arrow, 8=Sell Arrow, 9=ATR(仅计算)。EA可读取7/8号箭头缓冲判断买卖信号。
</content>
</invoke>
BIN
View File
Binary file not shown.