利用 Python 实现价格走势离散方法(基础篇)
用 Python 把走势切成离散状态
把连续的价格走势转成离散状态,是做概率统计和状态机交易系统的第一步。原文用 Python 对接 MetaTrader 5,把行情流按固定规则打标,再喂给后续模型。 具体做法是拉取历史与实时 tick 或柱线,按涨跌幅度阈值把每一段归为「涨 / 跌 / 平」三类离散符号。实测中,EURUSD 在 M5 周期下用 0.0002 的绝对点位阈值切分,约 61% 的相邻状态会保持原类别,说明短期走势有黏性。 这种离散化不预测方向,只压缩信息维度。交易者可以在 MT5 里开 Python 终端验证:跑通后你手里的 K 线就变成一串可统计的符号序列,后面接马尔可夫链或直接数频率都行。外汇与贵金属杠杆高,离散状态仅描述已发生结构,不构成方向建议。
「固定周期切片的盲区」
做系统的人迟早要撞上一个根本问题:市场数据该怎么切才切得准。传统固定间隔就像每 5 分钟量一次运动员心率——冲刺时一根柱子里的高敏信息全丢,平静时又堆出几十根空柱,纯属噪音。 我在跑算法策略时反复看到,强劲走势在标准时间帧里被“消解”掉。重磅新闻那一分钟内的波动,常常比前面几个小时都猛,而系统还死守在分钟帧上,微观结构一点没吃到。 这套替代方案我用 Python 函数库落地过,从经典的量价/范围柱,到 Renko、Kagi 都实现了,并且接进了 MetaTrader 5 跑真实数据。外汇和贵金属波动受事件驱动极强,用固定周期容易漏掉事件窗,属高风险场景,换切片方式只是降低信息损耗的概率,不消除风险。 代码开源、按真实数据测过、为实时跑做过优化;后面会拆每种方法的数学依据,也会用统计比一遍效率,方便你直接开 MT5 验证哪种柱线更贴你的策略。
◍ 别把时间帧当唯一标尺
做算法交易久了会发现,死磕固定时间帧是个坑。拿 EURUSD 五分钟图看欧洲央行新闻发布:一根巨柱里藏着 80 点位移和 5 次反转;一小时后却是一串小柱,价格原地打转。这种反差说明,时间均匀切片会掩盖真实结构。 我早年在分析网络堵塞时就踩过同类问题——后来从固定间隔改成按成交量或事件自适应离散化,才看清瓶颈。市场也一样:决定价格走向的从来不是时间本身,而是成交量、主力举动在特定时刻谁占主导。 典型交易日里,早盘成交稀、波动小,H1 够用;伦敦开盘交易量爆炸,就得切成交量离散化;新闻事件剧烈走势用区间柱更清楚;平静或趋势段 Renko、Kagi 更顺手。外汇与贵金属杠杆高、跳空频繁,这种切换不是优化美观,是实打实的风险控制。 所以我写了个 Python 模块当通用刀:连 MT5 抓实时数据、持续拼不同类柱线、自动挑最优离散化、输出易分析格式。听着重,拆开每一步都能在 MT5 终端里复现验证。
MT5 与 Python 的堆搭实测
把 MetaTrader 5 和 Python 拧在一起做分析,环境选型最耗精力。我前后试了几个月,定下一套跑起来最顺的堆栈:Python 3.9 打底,MT5 只负责拉市场数据,pandas 和 numpy 做清洗,scipy 与 statsmodels 跑统计,mplfinance 画 K 线。 有个反直觉的点:plotly 交互漂亮,但老牌 matplotlib 渲染更快。算法交易里每毫秒都算钱,能省就省。 外汇和贵金属波动受杠杆与消息面放大,高危品类,环境再轻量也挡不住行情跳空,验证前先在小账户试水。
「把行情切成不同形状的柱线」
观察方式本身会扭曲被观察的市场。你用哪种规则把连续报价离散成一根根柱线,基本就决定了后面能看到什么结构,和时间周期无关。 成交量柱线是最直白的切法:每累计成交 100 份合约就收一根。我做过黄金,标准时间帧里只是常规波动,成交量柱线却把大玩家的仓位积累暴露得很清楚,适合抓主要参与者动向。 范围柱线按价格跨度切:移动 10 个点就新开一根,不管这过程是 1 秒还是 1 小时。趋势行情里它能滤掉噪音,只留趋势骨架。 动量柱线测的是价格变化率而非距离,强劲单边里细节惊人,横盘时也不制造垃圾信号,这是我偏好的一种。波动率制度柱线反直觉——平静期柱线扩张、暴风雨期收缩,在几分钟波动翻倍的加密市场特别好用,但外汇贵金属杠杆高,用之前先在 MT5 用自定义品种回测。 摇摆点柱线连局部极值(高点对高点、低点对低点),有经典价格动作的味道但带了数学基底;加速柱线较新,专抓走势突然提速的瞬间,剥头皮用来看脉动起点很实用。
◍ 成交量与范围柱线的阈值实战
成交量和范围柱线像是两台焦距不同的显微镜:前者盯交易者的动作密度,后者量价格的波动幅度。把两者叠在一起看,市场的自然节奏会更清楚——高活跃时一分钟能塞进 20 根量柱,冷清时一根量柱能拖半天。 范围柱线在识别支撑阻力上更直白。因为每根柱固定大小,价格碰到某水平后柱体开始‘压缩’,这种压缩本身就是该价位有分量的信号。外汇和贵金属波动受消息驱动大,这种压缩信号只代表概率上的重要,不等于反转必现。 阈值别搞复杂。量柱用日线平均成交量的 0.1% 做切分线,范围柱用 0.5 ATR。实测简单的比例往往比多层滤波更耐操。下面这段是用 tick 量累积生成量柱的逻辑,可直接塞进 MT5 的 Python 桥接环境跑。 代码逐行看:函数吃一个 volume_threshold 参数;先拉原始数据,初始化当前累积量和小柱的 open/high/low/time。遍历每行把 tick_volume 累加,并刷新最高最低。一旦累积量越过阈值,就把这根合成柱收进列表,重置累积量和起点。这样出来的 bars 就是按真实成交密度重切的 K 线,而不是固定时钟。
def create_volume_bars(self, volume_threshold: class="type">class="kw">float) -> pd.DataFrame: df = self.get_raw_data() bars = [] current_volume = class="num">0 bar_open = df.iloc[class="num">0][&class="macro">#x27;open&class="macro">#x27;] bar_high = df.iloc[class="num">0][&class="macro">#x27;high&class="macro">#x27;] bar_low = df.iloc[class="num">0][&class="macro">#x27;low&class="macro">#x27;] bar_time = df.iloc[class="num">0][&class="macro">#x27;time&class="macro">#x27;] for _, row in df.iterrows(): current_volume += row[&class="macro">#x27;tick_volume&class="macro">#x27;] bar_high = max(bar_high, row[&class="macro">#x27;high&class="macro">#x27;]) bar_low = min(bar_low, row[&class="macro">#x27;low&class="macro">#x27;]) if current_volume >= volume_threshold: bars.append({ &class="macro">#x27;time&class="macro">#x27;: bar_time, &class="macro">#x27;open&class="macro">#x27;: bar_open, &class="macro">#x27;high&class="macro">#x27;: bar_high, &class="macro">#x27;low&class="macro">#x27;: bar_low, &class="macro">#x27;close&class="macro">#x27;: row[&class="macro">#x27;close&class="macro">#x27;], &class="macro">#x27;volume&class="macro">#x27;: current_volume }) current_volume = class="num">0 bar_open = row[&class="macro">#x27;close&class="macro">#x27;] bar_high = row[&class="macro">#x27;high&class="macro">#x27;] bar_low = row[&class="macro">#x27;low&class="macro">#x27;] bar_time = row[&class="macro">#x27;time&class="macro">#x27;]