纯 MQL5 货币对强弱指标·进阶篇
(2/3)· 从 SPairStrength 结构到气泡排序,把 28 对货币的混乱波动收敛成可读的强弱序列
◍ 给货币对发一张数字护照
做强度分析前,先得把每个货币对的状态装进同一个容器里。SPairStrength 这个结构就是干这个的:pair 存名字(如 EURUSD),strength 存综合强度值,h1_change / h4_change / d1_change 分别记小时、4小时、日线的变动量。 底层逻辑很直白——三个周期像三台不同焦距的摄像头,短期波动看 h1,中期节奏看 h4,全局趋势看 d1,最后揉成一个 strength 指数。外汇和贵金属波动大、杠杆高,这种多周期切片能帮你避开只用单周期导致的误判。 全局侧开了三个变量:g_strength_data[] 是结构数组,g_pairs_count 记货币对数量,g_timer_counter 管计时。InitializeDataArray() 里先用 ArraySize(CURRENCY_PAIRS) 定长度,再逐条把 strength 和三个 change 清零,相当于给每张护照盖了空白章。 开 MT5 把下面代码贴进 EA 或脚本,编译后加个 Print(g_pairs_count) 就能验证数组有没有按你 CURRENCY_PAIRS 列表的条数扩出来。
class=class="str">"cmt">// Structure for storing the strength of a currency pair class="kw">struct SPairStrength { class="type">class="kw">string pair; class=class="str">"cmt">// Currency pair class="type">class="kw">double strength; class=class="str">"cmt">// Current strength class="type">class="kw">double h1_change; class=class="str">"cmt">// Change per hour class="type">class="kw">double h4_change; class=class="str">"cmt">// Change in class="num">4 hours class="type">class="kw">double d1_change; class=class="str">"cmt">// Daily change }; class=class="str">"cmt">// Global variables SPairStrength g_strength_data[]; class="type">int g_pairs_count; class="type">int g_timer_counter; class=class="str">"cmt">// Initialize the data array class="type">void InitializeDataArray() { g_pairs_count = ArraySize(CURRENCY_PAIRS); ArrayResize(g_strength_data, g_pairs_count); class=class="str">"cmt">// Fill in basic information for(class="type">int i = class="num">0; i < g_pairs_count; i++) { g_strength_data[i].pair = CURRENCY_PAIRS[i]; g_strength_data[i].strength = class="num">0; g_strength_data[i].h1_change = class="num">0; g_strength_data[i].h4_change = class="num">0; g_strength_data[i].d1_change = class="num">0; } }
「面板分区与多周期权重的落地实现」
深色底(RGB 16,20,24)不是审美偏好,而是为了降低盯盘时的视觉疲劳;主面板固定在左上角,距边 20 像素,尺寸 800×400,这个尺度能在不挤占 K 线区的前提下排下强对 / 弱对两栏。 界面逻辑上切成两极:左侧放强势货币对(倾向做多),右侧放弱势货币对(倾向做空),像磁铁两极把注意力吸到最有概率出现的机会上。每栏表头固定打印「PAIR / H1 / H4 / D1 / FORCE」,让读者一眼读完三个周期的强度故事。 强度算法的权重分配是核心:D1 占 50% 反映全局趋势,H4 占 30% 管中期方向,H1 占 20% 捕捉短期扰动。相当于三级滤波,先把小时图噪音压下去,再让日线定调。 CreateGraphics 只搭骨架,真正的「活」要等下一节灌入变动数据才看得到;现在开着 MT5 把下面代码丢进指标,能直接看到空面板和两栏标题。 外汇与贵金属杠杆高、滑点突发,面板只做概率参考,不构成方向保证。
class="type">bool CreateGraphics() { class=class="str">"cmt">// Main panel if(!ObjectCreate(class="num">0, "SPPanel_Main", OBJ_RECTANGLE_LABEL, class="num">0, class="num">0, class="num">0)) { Print("Error creating main panel: ", GetLastError()); class="kw">return false; } class=class="str">"cmt">// Customize the panel appearance ObjectSetInteger(class="num">0, "SPPanel_Main", OBJPROP_XDISTANCE, class="num">20); ObjectSetInteger(class="num">0, "SPPanel_Main", OBJPROP_YDISTANCE, class="num">20); ObjectSetInteger(class="num">0, "SPPanel_Main", OBJPROP_XSIZE, class="num">800); ObjectSetInteger(class="num">0, "SPPanel_Main", OBJPROP_YSIZE, class="num">400); ObjectSetInteger(class="num">0, "SPPanel_Main", OBJPROP_BGCOLOR, C&class="macro">#x27;class="num">16,class="num">20,class="num">24&class="macro">#x27;); ObjectSetInteger(class="num">0, "SPPanel_Main", OBJPROP_BORDER_COLOR, C&class="macro">#x27;class="num">29,class="num">31,class="num">34&class="macro">#x27;); ObjectSetInteger(class="num">0, "SPPanel_Main", OBJPROP_BORDER_TYPE, BORDER_FLAT); ObjectSetInteger(class="num">0, "SPPanel_Main", OBJPROP_CORNER, CORNER_LEFT_UPPER); ObjectSetInteger(class="num">0, "SPPanel_Main", OBJPROP_BACK, false); class=class="str">"cmt">// Create headings and markup CreateLabel("SPPanel_Strong", "STRONG PAIRS - LONG", class="num">30, class="num">30, InpTextColor, class="num">10, true); CreateLabel("SPPanel_Weak", "WEAK PAIRS - SHORT", class="num">420, class="num">30, InpTextColor, class="num">10, true); class="type">class="kw">string header = " PAIR H1 H4 D1 FORCE"; CreateLabel("SPPanel_Header1", header, class="num">30, class="num">60, clrGray, class="num">9); CreateLabel("SPPanel_Header2", header, class="num">420, class="num">60, clrGray, class="num">9); class="type">bool CreateLabel(const class="type">class="kw">string name, const class="type">class="kw">string text, const class="type">int x, const class="type">int y, const class="type">color clr, const class="type">int size, const class="type">bool is_bold=false) { if(!ObjectCreate(class="num">0, name, OBJ_LABEL, class="num">0, class="num">0, class="num">0)) { Print("Error creating label ", name, ": ", GetLastError());
用加权涨跌拼出货币对力气值
这段逻辑先把标签和文字样式落到图表左上角,再算各周期涨跌,最后揉成一个综合强度。标签函数里 OBJPROP_CORNER 设成 CORNER_LEFT_UPPER、ANCHOR 用 ANCHOR_LEFT_UPPER,意味着文字锚死在窗口左上,不会随滚动跑偏。 CalculateChange 取最近两根 K 线:rates[1].open 为前一根开盘,rates[0].close 为最新收盘,用 (收盘-前开)/前开*100 得出百分比涨跌。若 CopyRates 拿不到数据或前开为 0,直接返回 0.0,避免除零崩脚本。 真正决定「力气」的是 CalculateStrengths 里的权重分配:H1 占 0.2、H4 占 0.3、D1 占 0.5。也就是日线方向说了大半句话,小时线只当杂音。 把下面代码丢进 MT5 脚本,改 g_pairs_count 和 g_strength_data 里的品种,就能在日志里看到每个对的 strength 数值,外汇和贵金属波动大、杠杆高,这种强度只是概率参考,别当方向保证。
class="kw">return false; } ObjectSetString(class="num">0, name, OBJPROP_TEXT, text); ObjectSetInteger(class="num">0, name, OBJPROP_XDISTANCE, x); ObjectSetInteger(class="num">0, name, OBJPROP_YDISTANCE, y); ObjectSetString(class="num">0, name, OBJPROP_FONT, is_bold ? "Arial Bold" : "Arial"); ObjectSetInteger(class="num">0, name, OBJPROP_FONTSIZE, size); ObjectSetInteger(class="num">0, name, OBJPROP_COLOR, clr); ObjectSetInteger(class="num">0, name, OBJPROP_CORNER, CORNER_LEFT_UPPER); ObjectSetInteger(class="num">0, name, OBJPROP_ANCHOR, ANCHOR_LEFT_UPPER); class="kw">return true; } class=class="str">"cmt">// Calculate the price change for a given timeframe class="type">class="kw">double CalculateChange(const class="type">class="kw">string symbol, const ENUM_TIMEFRAMES timeframe) { class="type">MqlRates rates[]; ArraySetAsSeries(rates, true); if(CopyRates(symbol, timeframe, class="num">0, class="num">2, rates) <= class="num">0) { class="kw">return class="num">0.0; } if(rates[class="num">1].open == class="num">0) class="kw">return class="num">0.0; class="kw">return ((rates[class="num">0].close - rates[class="num">1].open) / rates[class="num">1].open) * class="num">100.0; } class=class="str">"cmt">// Calculate the strength of all currency pairs class="type">void CalculateStrengths() { for(class="type">int i = class="num">0; i < g_pairs_count; i++) { class=class="str">"cmt">// Calculate changes across different timeframes g_strength_data[i].h1_change = CalculateChange(g_strength_data[i].pair, PERIOD_H1); g_strength_data[i].h4_change = CalculateChange(g_strength_data[i].pair, PERIOD_H4); g_strength_data[i].d1_change = CalculateChange(g_strength_data[i].pair, PERIOD_D1); class=class="str">"cmt">// Calculate the total force(weighted value) g_strength_data[i].strength = g_strength_data[i].h1_change * class="num">0.2 + class=class="str">"cmt">// class="num">20% weight of the hourly chart g_strength_data[i].h4_change * class="num">0.3 + class=class="str">"cmt">// class="num">30% weight of the class="num">4-hour chart g_strength_data[i].d1_change * class="num">0.5; class=class="str">"cmt">// class="num">50% weight of the daily chart } }
◍ 用冒泡思路给货币对排座次
把几十个外汇或贵金属货币对按强弱排队,核心不是炫算法,而是让 MT5 在每次刷新后立刻知道谁强谁弱。下面这段排序逻辑就是最直白的双层循环比较:内层拿 j 位置和 i 位置比 strength 字段,只要后者更强就交换,跑完外层一轮,数组头部自然堆着强势对,尾部是弱势对。 GetTopPairs 的取数方式很省事:不重排,直接按头部取前 count(默认 10)个进 strong 数组,按尾部倒序取进 weak 数组。这意味着你随时能拿到「前 10 强 / 前 10 弱」而无需再算一遍,对刷 new tick 的高频场景,每少一次全量排序就少耗几毫秒。 UpdateDisplay 把前 10 强打印成带 H1/H4/D1 涨跌幅和综合 strength 的字符串,格式 %+.1f 让正负一目了然。实盘接贵金属或交叉盘时,这种强弱分组只是概率参考,杠杆品种回撤可能极快,别把它当方向保证。 让小布替你跑这套 把 SortStrengthData 和 GetTopPairs 直接塞进 EA 的 OnTick 后段,用 Comment 或面板看前 10 强弱名单;若你盯 EURUSD 和 XAUUSD,可把 count 调到 5 观察头部是否被这两个常驻。外汇与贵金属波动受消息驱动,分组结果仅反映历史强度,不预示下一根 K 线。
class="type">void SortStrengthData() { for(class="type">int i = class="num">0; i < g_pairs_count - class="num">1; i++) { for(class="type">int j = i + class="num">1; j < g_pairs_count; j++) { if(g_strength_data[j].strength > g_strength_data[i].strength) { SPairStrength temp = g_strength_data[i]; g_strength_data[i] = g_strength_data[j]; g_strength_data[j] = temp; } } } } class="type">void GetTopPairs(SPairStrength &strong[], SPairStrength &weak[], class="type">int count = class="num">10) { ArrayResize(strong, count); for(class="type">int i = class="num">0; i < count; i++) { strong[i] = g_strength_data[i]; } ArrayResize(weak, count); for(class="type">int i = class="num">0; i < count; i++) { weak[i] = g_strength_data[g_pairs_count - class="num">1 - i]; } } class="type">void UpdateDisplay() { for(class="type">int i = class="num">0; i < class="num">10 && i < g_pairs_count; i++) { class="type">class="kw">string text = StringFormat("%s %+.1f %+.1f %+.1f %+.1f", g_strength_data[i].pair, g_strength_data[i].h1_change, g_strength_data[i].h4_change, g_strength_data[i].d1_change, g_strength_data[i].strength); UpdatePairDisplay("Strong_" + IntegerToString(i), text, InpStrongColor, i); } }