整理: 新增PriceAction_EngulfingZone_EA, 1h_eur_usd, 改良海龟三个EA; 新增SHK Heikin Ashi指标元数据; 修正文档-文件名保持原命名规则
This commit is contained in:
@@ -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
@@ -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
@@ -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;
|
||||
}
|
||||
//+------------------------------------------------------------------+
|
||||
@@ -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);
|
||||
}
|
||||
@@ -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>
|
||||
Reference in New Issue
Block a user