使用 MetaTrader 5 在 Python 中查找自定义货币对形态·进阶篇
🐍

使用 MetaTrader 5 在 Python 中查找自定义货币对形态·进阶篇

(2/3)· 接上篇环境打通,本篇用 6 个函数模块把历史形态变成可排序的交易参考

含代码示例偏理论 第 2/3 篇
很多人装好 MT5 和 Python 就直接跑现成脚本,却没搞清形态长度怎么影响噪声比。用固定 10 根 K 线找形态,大概率把随机波动当规律。本篇先把算法结构拆开,再谈怎么筛出值得看的形态。

把涨跌序列挖成可统计的形态库

上一环我们得到了纯“up/down”的涨跌标记序列,这一环要做的,是在这条序列里翻出能复用的局部形态。核心思路不复杂:取一段长度为 pattern_length(3 到 25 之间,例如 4)的连续方向作为“形态签名”,再看它后面第 6 根 K 线是否顺着我们关心的方向走,顺则记 True,否则 False。 函数用 defaultdict 建了一个 patterns 词典,键是形态元组,值是每次出现后 6 根结果的布尔列表。遍历时只跑到 len(data)-pattern_length-6,避免越界取不到“6 根之后”的判定。这样跑完,库里就存了诸如 ('up','up','down') 对应 [True, False, True…] 这样的原始命中记录。 真正有用的是尾部那段:拿最近 pattern_length 根的方向做成元组,去库里查是否出现过。命中了,就用 np.mean 算胜率百分比、用 len 算出现次数。外汇与贵金属市场高杠杆、跳空频繁,这种基于历史频次的概率仅作参考,实盘前请在 MT5 用自己品种数据重跑验证。 别把正态当圣经 代码里胜率直接 mean*100,背后假设的是每次形态出现相互独立且样本够密。小样本下某形态只出现 2 次却 100% 命中,这种数字在贵金属分钟线上极易误导,调参时先把 last_frequency 门槛拉到 20 以上再看 winrate。

MQL5 / C++
def find_patterns(data, pattern_length, direction):
    patterns = defaultdict(list)
    last_pattern = None
    last_winrate = None
    last_frequency = None
    for i in range(len(data) - pattern_length - class="num">6):
        pattern = tuple(data[&class="macro">#x27;direction&class="macro">#x27;][i:i+pattern_length])
        if data[&class="macro">#x27;direction&class="macro">#x27;][i+pattern_length+class="num">6] == direction:
            patterns[pattern].append(True)
        else:
            patterns[pattern].append(False)
    # Check last prices for pattern match
    last_pattern_tuple = tuple(data[&class="macro">#x27;direction&class="macro">#x27;][-pattern_length:])
    if last_pattern_tuple in patterns:
        last_winrate = np.mean(patterns[last_pattern_tuple]) * class="num">100
        last_frequency = len(patterns[last_pattern_tuple])
        last_pattern = last_pattern_tuple
    class="kw">return patterns, last_pattern, last_winrate, last_frequency

◍ 用胜率和频次给形态排座次

形态攒了一大堆,下一步不是全用,而是先筛。核心看两个量:胜率 = 该形态历史触发后指向的结果里“True”占比,频次 = 它总共出现了几次。 举个实在例子:某个形态 10 次里有 7 次奏效,胜率就是 70%;另一个形态胜率 65%,但只出现过 3 次,那它的统计置信就明显弱于前者。频次越高,样本越不容易被偶发波动带偏。 下面这段逻辑把每个形态的胜率和频次算出来塞进 results,再按胜率从高往低排,最好的形态自然浮到列表头顶。MT5 里跑完这类脚本,直接读排序后的前几条就能定下一步观察对象。外汇和贵金属波动受事件驱动明显,这类统计只代表历史概率,实盘仍属高风险。

MQL5 / C++
def calculate_winrate_and_frequency(patterns):
    results = []
    for pattern, outcomes in patterns.items():
        winrate = np.mean(outcomes) * class="num">100
        frequency = len(outcomes)
        results.append((pattern, winrate, frequency))
    results.sort(key=lambda x: x[class="num">1], reverse=True)
    class="kw">return results

「用出现次数和胜率筛掉废形态」

形态库跑完之后,原始数据量往往破千,但里面混着大量低质信号。统计上看,出现次数少于 20 次的形态可靠性明显偏低,这类样本太小,容易是噪声而非规律,直接剔除比较稳妥。 剩下的形态按胜率从高到低排,头部那批才是值得盯的。实测中从一千多个里筛完,能进前 300 的买/卖形态各自只剩三百条左右,数量砍掉七成以上,盘面上要跟踪的对象一下子清爽了。 下面这段是过滤与排序的核心逻辑,result 里下标 2 是出现次数、下标 1 是胜率,过 20 次才留,再逆序取前 300。 别把千个形态都当回事 MT5 里回测时若发现某形态只触发了几次就高胜率,别急着信,小样本在外汇和贵金属这种高波动市场里极易翻车,先卡 20 次门槛再谈概率。

MQL5 / C++
filtered_buy_results = [result for result in all_buy_results if result[class="num">2] > class="num">20]
filtered_sell_results = [result for result in all_sell_results if result[class="num">2] > class="num">20]
filtered_buy_results.sort(key=lambda x: x[class="num">1], reverse=True)
top_300_buy_patterns = filtered_buy_results[:class="num">300]
filtered_sell_results.sort(key=lambda x: x[class="num">1], reverse=True)
top_300_sell_patterns = filtered_sell_results[:class="num">300]

形态长度从3到25都要测

筛选有效价格形态时,长度窗口不能只拍脑袋定一个值。少于 3 次价格变动的形态噪声太大、统计意义弱;超过 25 次的形态在实盘样本里出现频率过低,难以持续产生可交易信号。因此把长度从 3 到 25 逐一跑一遍,才算把短周期反转与中长期趋势两类机会都覆盖到。 短形态偏向抓取快速逆转,例如欧元兑美元在新闻行情里 3~5 根 K 线内的急转;长形态更反映一段趋势的铺垫结构,比如黄金日线 20 根以上形成的缓慢顶部。事先没法断定哪类更划算,只能全长度扫描买入与卖出形态,再比对胜率和出现频次。 下面这段代码就是遍历逻辑的核心:对每个长度调用形态查找函数,分别存买入和卖出字典。你在 MT5 的 Python 环境或自建回测框架里直接套用,把 ohlc_data 换成自己的报价序列即可。外汇与贵金属杠杆高,回测盈利不代表实盘概率,参数验证后仍需小仓试错。

MQL5 / C++
pattern_lengths = range(class="num">3, class="num">25)  # Pattern lengths from class="num">3 to class="num">25
all_buy_patterns = {}
all_sell_patterns = {}
for pattern_length in pattern_lengths:
    buy_patterns, last_buy_pattern, last_buy_winrate, last_buy_frequency = find_patterns(ohlc_data, pattern_length, &class="macro">#x27;up&class="macro">#x27;)
    sell_patterns, last_sell_pattern, last_sell_winrate, last_sell_frequency = find_patterns(ohlc_data, pattern_length, &class="macro">#x27;down&class="macro">#x27;)
    all_buy_patterns[pattern_length] = buy_patterns
    all_sell_patterns[pattern_length] = sell_patterns

◍ 区分买卖形态的有效边界

形态库按长度分好类之后,下一步不是直接上实盘,而是先验证哪类形态在买、卖两个方向上真的有统计优势。市场上涨和下跌的微观结构常常不对称,同一组形态在多头和空头侧的胜率、出现频率可能差出一大截,所以不能把买卖混在一起算总账。 代码里把 all_buy_patterns 和 all_sell_patterns 按 pattern_length 拆开,各自跑 calculate_winrate_and_frequency,再把结果分别塞进 all_buy_results 与 all_sell_results。这一步等于给后续比对留出了干净的样本容器,买卖两边互不影响。 短期形态往往更适合抓入场点,长期形态偏向确认趋势方向,但反过来也有概率出现,所以全部长度都保留、不提前砍掉任何一类。分析完能得到一张表:哪种长度、在哪个市场状态下更偏买入有效或卖出有效。 外汇与贵金属属高杠杆品种,即便筛选出最优形态也只是提升概率,不保证成功。开 MT5 把这段循环接进你的形态提取模块,先跑一遍买卖分离的统计,再谈策略组装。

MQL5 / C++
all_buy_results = []
for pattern_length, patterns in all_buy_patterns.items():
    results = calculate_winrate_and_frequency(patterns)
    all_buy_results.extend(results)
all_sell_results = []
for pattern_length, patterns in all_sell_patterns.items():
    results = calculate_winrate_and_frequency(patterns)
    all_sell_results.extend(results)

「把历史形态变成可执行的预测输出」

EA 在跑完近期形态识别后,会把最后捕获的买入或卖出形态直接打印出来,同时附带两个硬指标:胜率和出现频率。胜率代表该形态在历史样本里后续走势符合预期的比例,比如某买入形态胜率 70%,即统计上七成概率价格随后上行;但哪怕冲到 90%,也只是概率,外汇和贵金属的高杠杆环境里黑天鹅随时能把统计砸穿。 频率是另一道过滤器。高频形态的样本量更大,统计置信度通常比罕见形态更稳,所以同样胜率下,常出现的形态更值得放进观察列表。代码里对买卖两种形态分别判断,是因为多空对抗本身就是常态,不为两边留接口就等于盲目押注。 预测行的输出只有一句「Price will likely go up / down」,它本质是历史数据的条件概率陈述,不是进场指令。MT5 上把这段逻辑接进 EA 的 OnTick 或信号模块,你就能在日志里看到实时诊断;真要下单,得自己叠宏观事件过滤和仓位尺度。 别把正态当圣经 胜率再漂亮也救不了突发利率决议,贵金属和外汇品种建议先在策略测试器用最近 3 个月 tick 数据回放一遍,确认频率没掉到个位数再谈信任。

MQL5 / C++
if last_buy_pattern:
    print(f"\nLast buy pattern for {symbol}: {last_buy_pattern}, Winrate: {last_buy_winrate:.2f}%, Frequency: {last_buy_frequency}")
    print(f"Forecast: Price will likely go up.")
if last_sell_pattern:
    print(f"\nLast sell pattern for {symbol}: {last_sell_pattern}, Winrate: {last_sell_winrate:.2f}%, Frequency: {last_sell_frequency}")
    print(f"Forecast: Price will likely go down.")
把形态回测交给小布盯盘
这些诊断小布盯盘的 AIGC 已内置,打开对应品种页即可看到相似历史形态的胜率和频率,你只需判断当前环境是否匹配。

常见问题

原文测试区间从 3 到 25,短长度噪声多、长长度样本少;实际可先跑 5~15 看胜率稳定性,再按品种波动调整。
不能。外汇贵金属高风险,胜率来自历史样本,未来可能失效,仅作概率参考而非确定性信号。
目前小布内置的是基于 MT5 数据的形态诊断视图,逻辑近似本篇算法,但无需你自己写 pandas 代码,打开品种页就能看。
因各品种波动节奏和交易时段不同,形态重复密度天然有异;偏理论看,需按品种单独标定长度区间。
按发生频率与胜率综合排序,原文 process_currency_pair 会输出买入和卖出前排形态供人工复核。