价格行为分析工具包开发(第 32 部分):基于 Python 的 K 线识别引擎(二)—— 使用 TA-Lib 进行检测
内容
引言
在上一篇文章中,我介绍了如何利用 Python 进行K线形态识别。我们首先采用手动识别方式,对每一种K线形态进行逐一硬编码实现。该实现逻辑为:每当 MQL5 向 Python 传递开盘价、收盘价、最高价、最低价及时间戳等行情数据时,Python 对数据进行运算处理,从而识别K线形态。
本文将对研究方法进行升级,引入 Python 版 TA-Lib 库,该库可自动识别60 余种K线形态。同时结合 mplfinance 与 matplotlib 库绘制K线图。
以下为所用核心库的整体介绍:
from flask import Flask, reque st, jsonify, send_file import numpy as np import pandas as pd import talib import json import matplotlib.pyplot as plt import mplfinance as mpf
下面逐项说明这些库的作用
Flask
from flask import Flask, request, jsonify, send_file
- 一款用于 Python 的轻量级 Web 框架,可用于开发 Web 应用程序或 API 接口。
- 用于创建 Flask 应用实例的核心类。
jsonify:将 Python 数据结构转换为 JSON 格式,用于接口响应。
send_file:将文件从服务器发送至客户端,适用于生成的图片或报告下载场景。
导入 numpy 库并命名为 np
- NumPy:Python 中用于数值计算的基础核心库。
- np:NumPy 的通用别名。
该库可高效处理数组运算、数学函数计算及数据操作。
导入 pandas 库并命名为 pd
- Pandas:一款功能强大的数据处理库。
- pd:标准别名。
提供 DataFrame 等数据结构,非常适合处理K线等时间序列数据。
导入 talib 库
- TA-Lib:Python 技术分析库。
内置函数可自动计算各类技术指标,并实现K线形态识别算法。
导入 json 库
- Python 处理 JSON 数据的内置标准库。
可解析请求中的 JSON 数据,或将 Python 对象转换为 JSON 格式用于响应。
导入 matplotlib.pyplot 模块并命名为 plt
- Matplotlib:数据可视化绘图库。
- pyplot:Matplotlib 的核心模块,提供类 MATLAB 的操作接口。
- plt:标准别名。
- 用于绘制静态、动态或交互式图表,包括K线图。
导入 mplfinance 库并命名为 mpf
- mplfinance:金融数据专用可视化库,专为K线图、OHLC图设计。
- mpf:便捷使用的别名。
- 可快速绘制金融图表,并支持丰富的自定义配置。
以下是使用 TA-Lib 进行形态识别的优势:
| 优势 | 说明 |
|---|---|
| 自动化 | 无需手动编码,可自动识别 60 余种K线形态。 |
| 高效性 | 形态识别速度更快,适用于实时行情分析。 |
| 准确率 | 采用经过充分验证的算法,识别结果稳定可靠。 |
| 兼容性 | 可轻松对接其他 Python 库,实现数据分析与可视化。 |
| 易用性 | 通过简洁的函数调用,简化开发实现流程。 |
承接上文内容
正如我此前所述,上一版系统是基于 Python 手动编码实现的K线识别系统。我们先来回顾一下该系统的整体流程。
- MetaTrader 5 EA(图表端调度器)
在 MT5 平台中,EA会监控每一根新生成的 K 线,并立即采集最近 31 根 K 线的开盘价、最高价、最低价、收盘价及时间戳数据。将数据封装为精简的 JSON 格式,通过 WebRequest 发送至本地的形态识别服务。当服务器返回K线形态名称数组时,
MQL5 EA →(OHLC 数据 JSON POST 请求)→ Python 服务器
EA 会按照设定的字体大小和颜色,在 K 线最高价位置自动标注形态标签,并对有效形态触发 MT5 弹窗提醒。每一次报价更新都会清除旧标签,保持图表整洁;脚本卸载时会清空所有绘图对象,无残留。
MQL5 EA:标注标签 + 触发提醒
- Python Flask 服务器:形态识别引擎
在后端,我基于 Flask 框架开发了应用程序,提供 /patterns 接口。接口接收 OHLC JSON 数据,将 Unix 时间戳转换为 UTC 时间,并构建 pandas DataFrame 数据结构以实现稳定索引。随后通过纯 Python 代码实现全套经典K线形态判断规则,包括十字星、锤子线、吞没形态、孕线、启明星/黄昏星等,每个形态都对应清晰、可测试的独立函数。
def detect_patterns(df): pats = ["None"]*len(df) for i in range(len(df)): o,h,l,c = df.iloc[i][["OPEN","HIGH","LOW","CLOSE"]] body = abs(c-o); rng = max(h-l,1e-6) lower = min(o,c)-l; upper = h-max(o,c) # doji if body/rng <= 0.1: pats[i] = "doji"; continue # hammer if lower>=2*body and upper<=body: pats[i] = "hammer"; continue # shooting star if upper>=2*body and lower<=body: pats[i] = "shootingstar"; continue # bullish engulfing if i>0: po,pc = df.iloc[i-1][["OPEN","CLOSE"]] if pc<po and c>o and o<=pc and c>=po: pats[i] = "bullishengulfing"; continue # bearish engulfing if i>0: po,pc = df.iloc[i-1][["OPEN","CLOSE"]] if pc>po and c<o and o>=pc and c<=po: pats[i] = "bearishengulfing"; continue # harami if i>0: po,pc = df.iloc[i-1][["OPEN","CLOSE"]] if pc<po and o<c and o>pc and c<po: pats[i] = "bullishharami"; continue if pc>po and o>c and o<pc and c>po: pats[i] = "bearishharami"; continue # morning star if i>1: o1,c1 = df.iloc[i-2][["OPEN","CLOSE"]] o2,c2 = df.iloc[i-1][["OPEN","CLOSE"]] if c1<o1 and abs(c2-o2)<(df.iloc[i-1]["HIGH"]-df.iloc[i-1]["LOW"])*0.3 \ and c>o2 and c>(o1+c1)/2: pats[i] = "morningstar"; continue # evening star if i>1: o1,c1 = df.iloc[i-2][["OPEN","CLOSE"]] o2,c2 = df.iloc[i-1][["OPEN","CLOSE"]] if c1>o1 and abs(c2-o2)<(df.iloc[i-1]["HIGH"]-df.iloc[i-1]["LOW"])*0.3 \ and c<o2 and c<(o1+c1)/2: pats[i] = "eveningstar"; continue return pats
在完成所有 K 线的形态标注后,我会统计所有非空形态的出现次数,生成一份简要日志摘要,并以毫秒为单位统计处理耗时。最后,返回一个 JSON 对象,其中包含完整的形态名称列表(每根 K 线对应一个结果)和上述日志。这种分工方式将繁重的数据处理任务交由 Python 执行 —— 这也是最便于维护和扩展的方案 —— 而 MetaTrader 5 则专注于图表输入输出与可视化展示。
Python 服务器 →(形态数据与日志 JSON)→ MQL5 EATA-Lib 库详解
TA-Lib(技术分析库)是一款开源库,被交易员、投资者和分析师广泛用于执行复杂技术计算与开发交易策略。该库最初由马里奥・福尔捷(Mario Fortier)开发,核心基于 ANSI C 语言编写:内置 200 余种技术指标(如 ADX、MACD、RSI(相对强弱指数)、随机指标、布林带等);同时原生支持识别 60 余种 K 线形态。其 C/C++ 核心提供了 API 接口,可通过 Python 调用,轻松集成至各类应用程序中。该库自 2001 年以 BSD 开源协议发布以来,已成为稳定可靠的工具,其算法经过时间验证,至今仍广泛应用于开源与商业量化项目中。
在本系统中,TA-Lib 丰富的形态识别功能被集成到基于 Python 的分析流程中,实现K线形态的自动化检测。系统从 TA-Lib 中动态加载所有相关的K线形态函数,可在接收的行情数据中识别出 60 余种不同形态。通过结合 TA-Lib 的可靠算法与自定义过滤逻辑,系统能精准识别看涨和看跌信号,再通过 mplfinance 库将信号直观叠加显示在K线上。整套流程封装在 Flask 网络服务中,支持实时数据处理、形态识别与可视化展示。该架构展示了如何将 TA-Lib 与现代 Python 工具结合,构建高效的自动化交易分析系统,并与 MQL5 策略顺畅协同。
电脑端 TA-Lib 分步安装指南
第一步:下载并安装 TA-Lib .whl 安装包
- Windows 系统:
从可信来源下载预编译的二进制文件。例如下载适配的.whl 文件,如TA_Lib‑0.4.0‑cp39‑cp39‑win_amd64.whl,该文件兼容 64 位 Windows 系统 + Python 3.9。“适配”指所选文件必须同时匹配你的 Python 版本和系统架构,确保兼容性。
通过 pip 命令安装:
pip install path\to\your\downloaded\file.whl
- macOS 系统:
通过 Homebrew 安装 TA-Lib 底层 C 语言库
brew install ta-lib
- Linux 系统(Debian/Ubuntu):
通过 apt 安装依赖库
sudo apt-get update sudo apt-get install libta-lib0-dev
第二步:安装 TA-Lib Python 封装包
底层 C 语言库安装完成后,执行以下命令安装 Python 调用接口:
pip install ta-lib
第三步:验证安装
打开 Python 交互环境,运行以下代码:
import talib print(talib.__version__)
若无报错且正常输出版本号,即代表安装成功。
接下来,我们查看已安装的 TA-Lib 库中支持的所有K线形态。
CDL2CROWS CDL3BLACKCROWS CDL3INSIDE CDL3LINESTRIKE CDL3OUTSIDE CDL3STARSINSOUTH CDL3WHITESOLDIERS CDLABANDONEDBABY CDLADVANCEBLOCK CDLBELTHOLD CDLBREAKAWAY CDLCLOSINGMARUBOZU CDLCONCEALBABYSWALL CDLCOUNTERATTACK CDLDARKCLOUDCOVER CDLDOJI CDLDOJISTAR CDLDRAGONFLYDOJI CDLENGULFING CDLEVENINGDOJISTAR CDLEVENINGSTAR CDLGAPSIDESIDEWHITE CDLGRAVESTONEDOJI CDLHAMMER CDLHANGINGMAN CDLHARAMI CDLHARAMICROSS CDLHIGHWAVE CDLHIKKAKE CDLHIKKAKEMOD CDLHOMINGPIGEON CDLIDENTICAL3CROWS CDLINNECK CDLINVERTEDHAMMER CDLKICKING CDLKICKINGBYLENGTH CDLLADDERBOTTOM CDLLONGLEGGEDDOJI CDLLONGLINE CDLMARUBOZU CDLMATCHINGLOW CDLMATHOLD CDLMORNINGDOJISTAR CDLMORNINGSTAR CDLONNECK CDLPIERCING CDLRICKSHAWMAN CDLRISEFALL3METHODS CDLSEPARATINGLINES CDLSHOOTINGSTAR CDLSHORTLINE CDLSPINNINGTOP CDLSTALLEDPATTERN CDLSTICKSANDWICH CDLTAKURI CDLTASUKIGAP CDLTHRUSTING CDLTRISTAR CDLUNIQUE3RIVER CDLUPSIDEGAP2CROWS CDLXSIDEGAP3METHODS
虽然大多数交易者都认识十字星、吞没形态等经典 K 线形态,但还有若干小众形态能够揭示市场情绪的微妙转折。弃婴形态是一种罕见的三根 K 线反转形态:一根十字星与前一根 K 线形成跳空缺口,随后又朝反方向再次跳空,整组 K 线如同被 “孤立遗弃”,往往预示行情即将出现强烈的趋势反转信号。夹板形态由两根同色实体较强的 K 线,中间夹着一根颜色相反的小实体 K 线构成;最后一根 K 线收盘价与第一根 K 线收盘价持平或极其接近。这种 “夹板” 结构说明:即便行情出现短暂反向波动,原有运行动能依然保持不变。续跳空形态形态属于三根 K 线的趋势延续形态:行情顺着原有方向先出现跳空,第三根 K 线仅部分回补缺口、并未完全封闭,体现市场不愿回补缺口的态度,进一步确认当前趋势强度。
另外两种更为少见的形态:诱骗形态(Hikkake)与腰带线形态(Belt Hold),能进一步丰富形态分析维度。源自日语命名的诱骗形态(Hikkake,意为陷阱),先出现一根内包线,随后形成假突破,引诱交易者顺势进场,之后行情反向突破区间另一侧边界,套牢逆势交易者,并助推原有趋势加速运行。与之不同,腰带线形态由单根决定性 K 线构成,能够顶住前期行情走势:下跌趋势中的腰带线形态,开盘于当日低位附近,随后强势收高;上涨趋势中的腰带线形态,开盘于当日高位附近,随后大幅收低。腰带线形态可在单根 K 线内体现多空力量的骤然切换,无需前一根 K 线被完全吞没;因此在关键支撑、阻力位,它是信号明确、影响力极强的反转形态。
在 TA-Lib 中,所有K线形态函数均以 CDL 作为前缀,是 Candle(蜡烛)的缩写。该前缀代表函数对应经典K线形态算法,例如:CDLDOJI 十字星、CDLENGULFING 吞没形态、CDLHAMMER 锤子线等。调用这类 CDL 系列函数时,传入开盘、最高、最低、收盘(OHLC)数组,函数会返回一组整数序列;非零数值代表出现对应 K 线形态,数值正负则代表看涨或看跌方向。
系统工作流程拆解
本节将带你梳理整套系统的运行逻辑,详细拆解 MQL5 与 Python 两端脚本流程。整套系统的核心是请求 — 响应闭环机制:每当一根新 K 线收盘完成,MQL5 EA会提取最近 60 根 K 线的开盘、最高、最低、收盘及时间戳数据,封装为精简的 JSON 数据包,以 POST 方式发送至本地 Flask 服务的 /patterns 接口。
随后 EA 等待服务器返回应答数据,应答包含两个长度均为 60 的并行数组:一个存放 K 线形态名称,一个标注看涨 / 看跌信号;EA 收到后立即解析 JSON 数据。回到 MetaTrader 5 端,程序先删除图表上所有旧标签,再在每一根出现有效形态的 K 线最高价位置重新绘制文字标注:看涨形态标注为青绿色,看跌形态标注为红色,同时触发带详细描述的 MT5 弹窗警报。
与此同时,Flask 接口接收传入的 JSON 数据,规整转换为 Pandas 数据框,调用 TA-Lib 六十余种形态识别算法逐根 K 线标记形态、判定多空偏向,最终返回两组形态与信号数组,同时附带运行日志和耗时统计指标。这种清晰的分工模式:MQL5 负责图表交互、绘图与警报推送,Python 负责数据规整与形态逻辑运算,让两端职责清晰、运行高效,且便于后续功能扩展迭代。

- MQL5 EA流程拆解
EA 初次加载时,会立即输出启动日志提示,提醒用户需在 MetaTrader 5 EA设置中,将 Flask 接口地址加入白名单。这是一次性配置步骤,配置完成后才能允许后续向本地形态识别服务发起 HTTP 请求,避免运行时静默报错、无任何提示。
int OnInit() { Log("EA started – allow WebRequest to: " + InpURL); return INIT_SUCCEEDED; }
每次有新报价到来时,EA 都会对比当前 K 线的时间戳与上一次已处理 K 线的时间戳。若自上次检测后没有新 K 线收盘,则直接退出本次逻辑,避免重复运算;确保每一根收盘 K 线只做一次形态分析。
void OnTick() { datetime bar = iTime(_Symbol, InpTF, 0); if(bar == 0 || bar == g_lastBar) return; g_lastBar = bar; // … (continue processing) }
一旦检测到新 K 线收盘确认,EA 便向前遍历最近 60 根 K 线,逐根提取开盘、最高、最低、收盘价以及 Unix 时间戳。这些数据先存入固定长度数组,再序列化为精简 JSON 对象,包含交易品种、时间周期,以及全部 60 组 OHLC 行情与时间数据。
double o[BARS], h[BARS], l[BARS], c[BARS]; long t[BARS]; for(int i = 0; i < BARS; i++) { int sh = i + 1; o[i] = iOpen(_Symbol, InpTF, sh); h[i] = iHigh(_Symbol, InpTF, sh); l[i] = iLow(_Symbol, InpTF, sh); c[i] = iClose(_Symbol, InpTF, sh); t[i] = (long)iTime(_Symbol, InpTF, sh); } string json = StringFormat( "{\"symbol\":\"%s\",\"timeframe\":%d,\"time\":[%s],\"open\":[%s],\"high\":[%s],\"low\":[%s],\"close\":[%s]}", _Symbol, InpTF, CSVInt(t), CSV(o), CSV(h), CSV(l), CSV(c) ); Log("JSON-OUT: " + json);
JSON 数据包组装完成后,EA 会向配置好的 Flask 地址发送 POST 请求,并在指定的超时时间内等待响应。 程序会捕获所有网络或权限类错误,若请求失败则输出详细日志;若请求成功,则接收服务器返回的 JSON 格式文本响应,用于后续处理。
char body[]; StringToCharArray(json, body, 0, StringLen(json)); char reply[]; string hdr = "Content-Type: application/json\r\n", respHdr; int code = WebRequest("POST", InpURL, hdr, InpTimeout, body, reply, respHdr); if(code == -1) { Log("WebRequest failed: " + IntegerToString(GetLastError())); return; } string resp = CharArrayToString(reply, 0, -1, CP_UTF8); Log("HTTP " + IntegerToString(code) + " RESP: " + resp);
HTTP 请求返回后,EA 会从响应数据中查找两个数组字段:一个名为 patterns(形态),一个名为 signals(信号)。通过简单的字符串检索与分割方法,将 JSON 数组还原为 MQL5 字符串数组,并校验两个数组的长度是否恰好为 60 个元素,校验通过后再继续执行后续逻辑。
string patTxt, sigTxt, patt[], sigs[]; if(!ExtractArray(resp, "patterns", patTxt) || !ExtractArray(resp, "signals", sigTxt) || !ParseArray(patTxt, patt) || !ParseArray(sigTxt, sigs) || ArraySize(patt) != BARS || ArraySize(sigs) != BARS) { Log("Malformed patterns or signals"); return; }
针对 60 根 K 线中的每一根,EA 都会检查对应的形态和信号数据。当检测到有效形态标记(即非 “None” 空值)时,程序会计算准确的 K 线偏移位置,清除旧的标注,并在该 K 线的最高价位置创建新的文本标签。标签通过颜色进行区分:青绿色代表看涨信号,红色代表看跌信号。每一个有效形态都会触发 MetaTrader 5 详细警报,包含交易品种、时间周期、信号方向、形态名称和时间戳。
for(int i = 0; i < BARS; i++) { string pat = patt[i], sig = sigs[i]; if(pat == "" || pat == "None") continue; int shift = BARS - i; datetime tm = iTime(_Symbol, InpTF, shift); double y = iHigh(_Symbol, InpTF, shift); string obj = PREFIX + IntegerToString(shift); ObjectCreate(0, obj, OBJ_TEXT, 0, tm, y); ObjectSetString(0, obj, OBJPROP_TEXT, pat); color col = (sig == "bullish" ? clrLime : clrRed); ObjectSetInteger(0, obj, OBJPROP_COLOR, col); ObjectSetInteger(0, obj, OBJPROP_FONTSIZE, InpFontSize); ObjectSetInteger(0, obj, OBJPROP_SELECTABLE, false); Alert(StringFormat("%s %s %s pattern '%s' at %s", _Symbol, EnumToString(InpTF), sig, pat, TimeToString(tm, TIME_DATE|TIME_MINUTES))); }
当EA被移除,或 MetaTrader 5 平台关闭时,卸载初始化程序会遍历图表上所有带有该 EA 专属前缀标记的对象,并将其全部删除。这能确保在 EA 停止运行后,图表上不会残留任何过期标注,保持你的交易界面整洁干净。
void OnDeinit(const int reason) { for(int i = ObjectsTotal(0,0,-1)-1; i >= 0; i--) { string n = ObjectName(0, i, 0, -1); if(StringFind(n, PREFIX) == 0) ObjectDelete(0, n); } Log("EA removed"); }
- Python Flask 服务器流程拆解
Flask 服务启动时,会配置INFO 级别日志,将所有传入请求与内部事件同时输出到控制台和循环日志文件中。紧接着,脚本会扫描 TA-Lib 库,并将所有以 CDL 开头的K线形态函数动态加载到内存字典中。这种动态发现机制意味着:服务会自动支持 TA-Lib 所有现有及未来新增的形态,无需手动修改代码;只要更新 TA-Lib 库,系统就能立刻使用新增形态。
app = Flask(__name__) logging.basicConfig(level=logging.INFO, format='%(asctime)s %(levelname)s %(message)s') app.logger.setLevel(logging.INFO) # load all TA‑Lib candlestick functions CDL_FUNCS = { name: getattr(talib, name) for name in talib.get_functions() if name.startswith("CDL") }
每当 EA 向 /patterns 接口发送 POST 请求时,服务器会读取原始请求数据,剔除 MetaTrader 5 可能追加的空字节,然后尝试将数据解码为 JSON 格式。若解析失败,或缺少必填数组(开盘价、最高价、最低价、收盘价、时间),服务器会立即返回清晰的 HTTP 400 错误并附带说明信息。在流程入口校验数据,可避免格式错误或不完整的数据进入处理流水线,防止产生难以排查的后续错误。
@app.route('/patterns', methods=['POST']) def patterns(): app.logger.info("Received /patterns request") try: raw = request.data if b'\x00' in raw: raw = raw.split(b'\x00', 1)[0] data = json.loads(raw.decode('utf-8')) except Exception as e: return jsonify(error="Invalid JSON", details=str(e)), 400
当JSON 数据通过校验后,服务器提取5 组核心数组:时间戳 + 4 组价格序列,并反转数组顺序,让索引 0 对应最早的 K 线。随后将 UNIX 时间戳转换为 pandas 时间索引,确保后续运算支持时间序列处理。若任意数组长度不一致、或包含非数值类型数据,服务会立即捕获异常并返回错误,确保只有格式完全规范、类型正确的数据才能进入形态识别逻辑。
try: symbol = data.get('symbol', 'Instrument') ts = data.get('time', []) open_ = np.array(data['open'][::-1], dtype=float) high = np.array(data['high'][::-1], dtype=float) low = np.array(data['low'][::-1], dtype=float) close = np.array(data['close'][::-1], dtype=float) idx = pd.to_datetime(np.array(ts[::-1], dtype='int64'), unit='s') app.logger.info(f"Loaded {len(open_)} bars for {symbol}") except KeyError as ke: return jsonify(error=f"Missing field {ke}"), 400 except Exception as e: return jsonify(error="Bad field format", details=str(e)), 400
获取到规整的 NumPy 数组后,服务程序会遍历 TA-Lib 中的每一个K线形态函数,并对全部 K 线序列执行识别检测。所有非零返回值都会被记录为候选形态命中结果。程序还会根据请求标记,有条件地调用两个额外的辅助函数 —— 一个用于校验孕线形态,另一个用于优化吞没形态 —— 在需要时执行更严格的识别标准。将每一次 TA-Lib 函数调用都置于 try/except 异常捕获机制中,意味着单个异常函数或数据格式错误不会导致整个服务崩溃;程序会直接跳过该函数,继续执行剩余逻辑。
n = len(open_) all_hits = [[] for _ in range(n)] for name, func in CDL_FUNCS.items(): try: res = func(open_, high, low, close) except Exception: continue for i, v in enumerate(res): if v == 0: continue if name == "CDLHARAMI" and fh and not is_manual_harami(open_, close, i, tol=HARAMI_TOL): continue if name == "CDLENGULFING" and fe and not is_manual_engulfing(open_, close, i, tol=ENGULF_TOL): continue all_hits[i].append((name, v))
同一根 K 线可能会同时匹配多种形态,此时服务程序会通过可配置的优先级列表选择主导形态 —— 优先级为:吞没形态 > 孕线 > 十字星。若优先级列表中的形态均未出现,则选择 TA-Lib 输出绝对值最大的形态。最终根据选中结果的数值正负,映射为看涨(bullish)或看跌(bearish)标签。这种每根 K 线仅保留一个确定形态的方案,能为每根 K 线打上清晰的信号,简化后续的图表标注和警报逻辑。
PRIORITY = ["CDLENGULFING", "CDLHARAMI", "CDLDOJI"] detected = [None] * n signals = [None] * n for i, hits in enumerate(all_hits): if not hits: continue pick = next(((nm, val) for pat in PRIORITY for nm, val in hits if nm == pat), None) if pick is None: pick = max(hits, key=lambda x: abs(x[1])) nm, val = pick detected[i] = nm signals[i] = "bullish" if val > 0 else "bearish"
接下来,脚本使用四组价格数组和时间索引构建 pandas 数据框,并新增两列:一列用于记录识别出的形态名称,另一列记录多空信号。为了进行可视化标注,程序会根据整体价格区间计算小幅偏移量,并构建散点标注列表:看涨信号在 K 线低点下方绘制绿色向上箭头,看跌信号在 K 线高点上方绘制红色向下箭头。 标记位置会根据图表动态价格区间自适应调整,避免视觉杂乱,确保在不同品种、不同时间周期下显示效果一致。
df = pd.DataFrame({"Open": open_, "High": high, "Low": low, "Close": close}, index=idx)
df["Pattern"] = pd.Series(detected, index=idx).fillna("None")
df["Signal"] = pd.Series(signals, index=idx).fillna("")
df.sort_index(inplace=True)
adds = []
price_rng = df["High"].max() - df["Low"].min()
for tstamp, row in df.iterrows():
if row["Pattern"] == "None":
continue
if row["Signal"] == "bullish":
y, marker, color = row["Low"] - price_rng*0.005, "^", "green"
else:
y, marker, color = row["High"] + price_rng*0.005, "v", "red"
adds.append(mpf.make_addplot(
[y if i == tstamp else np.nan for i in df.index],
type="scatter", marker=marker, markersize=80, color=color
))最后,服务程序定义自定义K线图配色方案(例如:阳线绿色、阴线红色),并通过 mplfinance 绘制完整K线图,同时叠加上述散点信号标记。图表会添加图例,用于说明 K 线颜色和形态标记的含义,方便直观解读。图表会调整至最佳清晰度,保存为服务器目录下唯一的 PNG 文件,随后关闭图像释放内存。JSON 响应会返回反转顺序后的形态与信号列表(使索引 0 对应最新 K 线)、可选的日志摘要,以及保存的图表文件名,可供 EA 直接使用或集成到其他分析工具中。
mc = make_marketcolors(up='green', down='red', edge='inherit', wick='inherit') style = make_mpf_style(marketcolors=mc, base_mpf_style='default') fig, axes = mpf.plot(df, type="candle", style=style, title=f"{symbol} Patterns", addplot=adds, volume=False, returnfig=True, tight_layout=True) axes[0].legend(handles=[ Line2D([0],[0],color='green',lw=4,label='Buy Candle'), Line2D([0],[0],color='red',lw=4,label='Sell Candle'), Line2D([0],[0],marker='^',linestyle='None',color='green',markersize=12,label='Buy Signal'), Line2D([0],[0],marker='v',linestyle='None',color='red',markersize=12,label='Sell Signal') ], loc='upper left', frameon=True) fname = f"pattern_chart_{uuid.uuid4().hex[:8]}.png" fig.savefig(path := os.path.join(os.path.dirname(__file__), fname), dpi=100) plt.close(fig) app.logger.info(f"Chart saved: {fname}") return jsonify( patterns=[p or "None" for p in detected[::-1]], signals =[s or "none" for s in signals [::-1]], log=[], chart=fname )
测试及结果
以下为本次实测结果展示。
阶梯指数品种实测
MQL5 运行日志
MQL5 智能交易选项卡会输出日志,记录发送至 Python 的数据、接收到的 K 线形态,以及每根当前识别 K 线的形态名称。每一根新 K 线收盘后会即时更新上述信息,并同步触发行情警报。
2025.07.15 22:56:50.294 Candlestick Label 2 (Step Index,M15) [CSLAB] JSON-OUT: {"symbol":"Step Index","timeframe":0,"time":[1752558300,1752559200, 1752560100,1752561000,1752561900,1752562800,1752563700,1752564600,1752565500,1752566400,1752567300,1752568200, 1752569100,1752570000,1752570900,1752571800,1752572700,1752573600,1752574500,1752575400,1752576300,1752577200,1752578100,1752579000, 1752579900,1752580800,1752581700,1752582600,1752583500,1752584400,1752585300,1752586200,1752587100,1752588000,1752588900,1752589800, 1752590700,1752591600,1752592500,1752593400,17525943 2025.07.15 22:56:50.804 Candlestick Label 2 (Step Index,M15) [CSLAB] HTTP 200 RESP: {"chart":"pattern_chart_e06f7a61.png","log":[],"patterns": ["CDLENGULFING","CDLHARAMI","None","CDLCLOSINGMARUBOZU","CDLHARAMI","CDLHIKKAKE","CDLENGULFING","CDLHIGHWAVE","None","CDLLONGLINE", "CDLHIGHWAVE","CDLSHORTLINE","CDLSHORTLINE","CDLHIKKAKE","CDLHARAMI","CDLENGULFING","CDLDOJI","None","None","None","CDLCLOSINGMARUBOZU", "None","CDLBELTHOLD","None","CDLHARAMI","CDLBELTHOLD","CDLHIKKAKE","CDLENGULFING","CDLHIKKAKE","CDLHIKKAKE","None","CDLDOJI","CDLHAMMER", "CDLHARAMI","CDLENGULFING" 2025.07.15 22:56:50.804 Candlestick Label 2 (Step Index,M15) 2025.07.15 22:56:50.804 Candlestick Label 2 (Step Index,M15) Alert: Step Index PERIOD_CURRENT bullish pattern 'CDLENGULFING' at 2025.07.15 05:45
命令行日志
- POST /patterns:客户端(你的 MQL5 EA)向 /patterns 接口发起了 HTTP POST 请求。
- HTTP/1.1:本次请求采用 HTTP 1.1 协议。
- 200:服务器返回 200 状态码,代表请求正常 —— 接口已成功完成请求处理。
2025-07-15 22:54:52,246 INFO Received /patterns request 2025-07-15 22:54:52,250 INFO Loaded 60 bars for Step Index 2025-07-15 22:54:52,808 INFO Chart saved: pattern_chart_a77f2eec.png 2025-07-15 22:54:52,810 INFO 127.0.0.1 - - [15/Jul/2025 22:54:52] "POST /patterns HTTP/1.1" 200 - 2025-07-15 22:55:00,040 INFO Received /patterns request
下图展示了检测到的形态,显示了“阶梯指数”随时间的变化情况。图表由 Matplotlib、TA-Lib 与 mplfinance 联合生成,同时展示行情 K 线以及本系统识别出的K线信号:
K线
- 绿色实体代表收盘价高于开盘价,即看涨阳线。
- 红色实体代表收盘价低于开盘价,即看跌阴线。
信号
- K 线下方的绿色标记代表买入信号(看涨形态)。
- K 线上方的红色标记代表卖出信号(看跌形态)。

Crash 1000 指数测试

MQL5 运行日志
2025.07.15 23:30:00.299 Candlestick Label 2 (Crash 1000 Index,M15) [CSLAB] JSON-OUT: {"symbol":"Crash 1000 Index","timeframe":0,"time":[1752561000,1752561900,
1752562800,1752563700,1752564600,1752565500,1752566400,1752567300,1752568200,1752569100,1752570000,1752570900,1752571800,1752572700,1752573600,1752574500,1752575400
,1752576300,1752577200,1752578100,1752579000,1752579900,1752580800,1752581700,1752582600,1752583500,1752584400,1752585300,1752586200,1752587100,1752588000,1752588900,
1752589800,1752590700,1752591600,1752592500,1752593400,1752594300,1752595200,1752596100,17
2025.07.15 23:30:02.749 Candlestick Label 2 (Crash 1000 Index,M15) [CSLAB] HTTP 200 RESP: {"chart":"pattern_chart_ebd72b47.png","log":[],"patterns":
["CDLCLOSINGMARUBOZU","CDLBELTHOLD","CDLBELTHOLD","None","CDLBELTHOLD","CDLHIKKAKE","CDLENGULFING","CDLBELTHOLD","CDLHARAMI","CDLENGULFING","None","CDLBELTHOLD",
"None","CDLBELTHOLD","CDLBELTHOLD","CDLBELTHOLD","None","CDLBELTHOLD","CDLBELTHOLD","None","CDLBELTHOLD","CDLBELTHOLD","CDLBELTHOLD","CDLDOJI","CDLBELTHOLD","None",
"CDLSHORTLINE","CDLSHORTLINE","None","CDLENGULFING","CDLHARAMI","CDLCLOSINGMARUBOZU","None","CDLMATCHINGL
2025.07.15 23:30:02.749 Candlestick Label 2 (Crash 1000 Index,M15)
2025.07.15 23:30:02.769 Candlestick Label 2 (Crash 1000 Index,M15) Alert: Crash 1000 Index PERIOD_CURRENT bearish pattern 'CDLCLOSINGMARUBOZU' at 2025.07.15 06:30
命令行日志
2025-07-15 23:30:02,719 INFO Chart saved: pattern_chart_ebd72b47.png 2025-07-15 23:30:02,735 INFO 127.0.0.1 - - [15/Jul/2025 23:30:02] "POST /patterns HTTP/1.1" 200 -
用Python绘制K线形态
该图表清晰展示了系统识别出的每一种形态如何发出信号,以及信号出现后的行情表现。

结论
在这套架构中,MetaTrader 5 专注发挥自身优势 —— 实时行情数据获取、图表对象管理与原生警报推送;而轻量级 Flask 服务则借助 TA-Lib 和 mplfinance 完成全部K线形态计算,并生成专业可视化图表。通过以 JSON 格式传输 60 根 K 线的 OHLC 价格与时间戳数据,EA 实现了亚秒级分析,且自身占用资源极少;Python 后端则输出精细的形态标注与完整的带标记图表,可直接用于查看或分享。
这种清晰的职责分工打造出真正模块化的系统:你可以独立优化形态识别规则、调整信号过滤精度、或扩展 EA 警报逻辑,互不干扰。将 Flask 服务打包部署(无论是放入 Docker 容器实现一致性部署,还是置于带认证的 API 网关之后),还能进一步提升可移植性与安全性。
我建议你将这套框架应用到自己常用的交易品种中,尝试叠加自定义指标,并把生成的图表集成到更全面的分析平台里。借助这种MQL5+Python 混合开发模式,你能同时发挥 MQL5 与现代 Python 库的全部能力,把人工看盘转化为自动化、高精度的量化分析工作流。
本文由MetaQuotes Ltd译自英文
原文地址: https://www.mql5.com/en/articles/18824
注意: MetaQuotes Ltd.将保留所有关于这些材料的权利。全部或部分复制或者转载这些材料将被禁止。
本文由网站的一位用户撰写,反映了他们的个人观点。MetaQuotes Ltd 不对所提供信息的准确性负责,也不对因使用所述解决方案、策略或建议而产生的任何后果负责。
MQL5 交易工具(第五部分):创建滚动行情条,实现交易品种实时监控
从基础到中级:结构(五)
MetaTrader 5 机器学习蓝图(第一部分):数据泄露与时间戳修正
MQL5自优化智能交易系统(第九部分):双移动平均线交叉