3D 柱线上的趋势强度和方向指标·进阶篇
📘

3D 柱线上的趋势强度和方向指标·进阶篇

第 2/3 篇

用三投影把趋势力读成桥

早期用 MatPlotLib 和 Plotly 堆 3D 图,结果不是像圣诞树就是缺关键细节,对交易毫无助益。后来借女儿积木桥的启发,放弃花哨立体图,改成同一时空的三层投影来排布信息。 最上层是常规蜡烛,仅作背景;中间层是趋势力(Trend Force)曲线,实测两个阈值最关键:等级 3 附近属于湍流区,趋势可能加速也可能逆转;等级 6 被突破时,大概率出现强劲单边走势。 底层把趋势方向与力值相乘画成柱线:绿色柱顶着高推力往上,是偏多的入场倾向;若力值已高于 6 且红色柱线攀升,则偏空信号更明确。外汇与贵金属波动剧烈,此类信号仅作概率参考,实盘须严控仓位。 下面这段 Python 用 make_subplots 把三窗口叠好,你可直接抄去连自己的 df 验证: <span class="keyword">def</span> create_visualization(self, df: pd.DataFrame = <span class="literal">None</span>): &nbsp;&nbsp;&nbsp;&nbsp;<span class="keyword">if</span> df <span class="keyword">is</span> <span class="literal">None</span>: &nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;df = self.analyze_market() &nbsp;&nbsp;&nbsp;&nbsp; &nbsp;&nbsp;&nbsp;&nbsp;df = df.reset_index() &nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; &nbsp;&nbsp;&nbsp;&nbsp;<span class="comment"># Three projections of our "bridge"</span> &nbsp;&nbsp;&nbsp;&nbsp;fig = make_subplots(rows=<span class="number">3</span>, cols=<span class="number">1</span>, &nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; shared_xaxes=<span class="literal">True</span>, &nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; subplot_titles=(<span class="string">'Price'</span>, <span class="string">'Trend Force'</span>, <span class="string">'Trend Direction'</span>), &nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; row_heights=[<span class="number">0.5</span>, <span class="number">0.25</span>, <span class="number">0.25</span>], &nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; vertical_spacing=<span class="number">0.05</span>) &nbsp;&nbsp;&nbsp;&nbsp;<span class="comment"># Main chart - classic candles</span> &nbsp;&nbsp;&nbsp;&nbsp;fig.add_trace( &nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;go.Candlestick( &nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;x=df[<span class="string">'time'</span>], &nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;<span class="built_in">open</span>=df[<span class="string">'open'</span>], &nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;high=df[<span class="string">'high'</span>], &nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;low=df[<span class="string">'low'</span>], &nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;close=df[<span class="string">'close'</span>], &nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;name=<span class="string">'OHLC'</span> &nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;), &nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;row=<span class="number">1</span>, col=<span class="number">1</span> &nbsp;&nbsp;&nbsp;&nbsp;) <span class="comment"># The trend force is our main feature</span> &nbsp;&nbsp;&nbsp;&nbsp;fig.add_trace( &nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;go.Scatter( &nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;x=df[<span class="string">'time'</span>], &nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;y=df[<span class="string">'trend_force_adjusted'</span>], &nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;mode=<span class="string">'lines'</span>, &nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;line=<span class="built_in">dict</span>(color=<span class="string">'blue'</span>, width=<span class="number">2</span>), &nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;name=<span class="string">'Trend Force'</span> &nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;), &nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;row=<span class="number">2</span>, col=<span class="number">1</span> &nbsp;&nbsp;&nbsp;&nbsp;) &nbsp;&nbsp;&nbsp;&nbsp;<span class="comment"># Reference levels</span> &nbsp;&nbsp;&nbsp;&nbsp;fig.add_hline(y=<span class="number">3</span>, line_dash=<span class="string">"dash"</span>, line_color=<span class="string">"yellow"</span>, row=<span class="number">2</span>, col=<span class="number">1</span>) &nbsp;&nbsp;&nbsp;&nbsp;fig.add_hline(y=<span class="number">6</span>, line_dash=<span class="string">"dash"</span>, line_color=<span class="string">"green"</span>, row=<span class="number">2</span>, col=<span class="number">1</span>) <span class="comment"># Trend direction as a derivative of force</span> &nbsp;&nbsp;&nbsp;&nbsp;fig.add_trace( &nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;go.Bar( &nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;x=df[<span class="string">'time'</span>], &nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;y=df[<span class="string">'trend_direction'</span>] * df[<span class="string">'trend_force_adjusted'</span>], &nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;name=<span class="string">'Trend Direction'</span>, &nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;marker_color=np.where(df[<span class="string">'trend_direction'</span>] &gt; <span class="number">0</span>, <span class="string">'green'</span>, <span class="string">'red'</span>) &nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;), &nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;row=<span class="number">3</span>, col=<span class="number">1</span> &nbsp;&nbsp;&nbsp;&nbsp;) 逐行看:函数先判 df 为空就跑 analyze_market 拿数据,reset_index 把时间提出来;make_subplots 建 3 行 1 列、高度比 0.5/0.25/0.25,共享 X 轴。第 1 行塞蜡烛图,第 2 行画 trend_force_adjusted 蓝线,并加 y=3 黄虚线、y=6 绿虚线作阈值。第 3 行用方向乘力值画柱,正为绿、负为红,把「力+方向」合成一眼可读的底栏。

MQL5 / C++
<span class="keyword">def</span> create_visualization(self, df: pd.DataFrame = <span class="literal">None</span>):
&nbsp;&nbsp;&nbsp;&nbsp;<span class="keyword">if</span> df <span class="keyword">is</span> <span class="literal">None</span>:
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;df = self.analyze_market()
&nbsp;&nbsp;&nbsp;&nbsp;
&nbsp;&nbsp;&nbsp;&nbsp;df = df.reset_index()
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;
&nbsp;&nbsp;&nbsp;&nbsp;<span class="comment"># Three projections of our "bridge"</span>
&nbsp;&nbsp;&nbsp;&nbsp;fig = make_subplots(rows=<span class="number">class="num">3</span>, cols=<span class="number">class="num">1</span>, 
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; shared_xaxes=<span class="literal">True</span>,
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; subplot_titles=(<span class="class="type">class="kw">string">&class="macro">#x27;Price&class="macro">#x27;</span>, <span class="class="type">class="kw">string">&class="macro">#x27;Trend Force&class="macro">#x27;</span>, <span class="class="type">class="kw">string">&class="macro">#x27;Trend Direction&class="macro">#x27;</span>),
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; row_heights=[<span class="number">class="num">0.5</span>, <span class="number">class="num">0.25</span>, <span class="number">class="num">0.25</span>],
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; vertical_spacing=<span class="number">class="num">0.05</span>)
&nbsp;&nbsp;&nbsp;&nbsp;<span class="comment"># Main chart - classic candles</span>
&nbsp;&nbsp;&nbsp;&nbsp;fig.add_trace(
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;go.Candlestick(
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;x=df[<span class="class="type">class="kw">string">&class="macro">#x27;time&class="macro">#x27;</span>],
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;<span class="built_in">open</span>=df[<span class="class="type">class="kw">string">&class="macro">#x27;open&class="macro">#x27;</span>],
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;high=df[<span class="class="type">class="kw">string">&class="macro">#x27;high&class="macro">#x27;</span>],
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;low=df[<span class="class="type">class="kw">string">&class="macro">#x27;low&class="macro">#x27;</span>],
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;close=df[<span class="class="type">class="kw">string">&class="macro">#x27;close&class="macro">#x27;</span>],
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;name=<span class="class="type">class="kw">string">&class="macro">#x27;OHLC&class="macro">#x27;</span>
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;),
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;row=<span class="number">class="num">1</span>, col=<span class="number">class="num">1</span>
&nbsp;&nbsp;&nbsp;&nbsp;)
<span class="comment"># The trend force is our main feature</span>
&nbsp;&nbsp;&nbsp;&nbsp;fig.add_trace(
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;go.Scatter(
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;x=df[<span class="class="type">class="kw">string">&class="macro">#x27;time&class="macro">#x27;</span>],
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;y=df[<span class="class="type">class="kw">string">&class="macro">#x27;trend_force_adjusted&class="macro">#x27;</span>],
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;mode=<span class="class="type">class="kw">string">&class="macro">#x27;lines&class="macro">#x27;</span>,
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;line=<span class="built_in">dict</span>(class="type">class="kw">color=<span class="class="type">class="kw">string">&class="macro">#x27;blue&class="macro">#x27;</span>, width=<span class="number">class="num">2</span>),
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;name=<span class="class="type">class="kw">string">&class="macro">#x27;Trend Force&class="macro">#x27;</span>
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;),
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;row=<span class="number">class="num">2</span>, col=<span class="number">class="num">1</span>
&nbsp;&nbsp;&nbsp;&nbsp;)
&nbsp;&nbsp;&nbsp;&nbsp;<span class="comment"># Reference levels</span>
&nbsp;&nbsp;&nbsp;&nbsp;fig.add_hline(y=<span class="number">class="num">3</span>, line_dash=<span class="class="type">class="kw">string">"dash"</span>, line_color=<span class="class="type">class="kw">string">"yellow"</span>, row=<span class="number">class="num">2</span>, col=<span class="number">class="num">1</span>)
&nbsp;&nbsp;&nbsp;&nbsp;fig.add_hline(y=<span class="number">class="num">6</span>, line_dash=<span class="class="type">class="kw">string">"dash"</span>, line_color=<span class="class="type">class="kw">string">"green"</span>, row=<span class="number">class="num">2</span>, col=<span class="number">class="num">1</span>)
<span class="comment"># Trend direction as a derivative of force</span>
&nbsp;&nbsp;&nbsp;&nbsp;fig.add_trace(
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;go.Bar(
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;x=df[<span class="class="type">class="kw">string">&class="macro">#x27;time&class="macro">#x27;</span>],
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;y=df[<span class="class="type">class="kw">string">&class="macro">#x27;trend_direction&class="macro">#x27;</span>] * df[<span class="class="type">class="kw">string">&class="macro">#x27;trend_force_adjusted&class="macro">#x27;</span>],
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;name=<span class="class="type">class="kw">string">&class="macro">#x27;Trend Direction&class="macro">#x27;</span>,
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;marker_color=np.where(df[<span class="class="type">class="kw">string">&class="macro">#x27;trend_direction&class="macro">#x27;</span>] &gt; <span class="number">class="num">0</span>, <span class="class="type">class="kw">string">&class="macro">#x27;green&class="macro">#x27;</span>, <span class="class="type">class="kw">string">&class="macro">#x27;red&class="macro">#x27;</span>)
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;),
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;row=<span class="number">class="num">3</span>, col=<span class="number">class="num">1</span>
&nbsp;&nbsp;&nbsp;&nbsp;)

「成交量在不同时段的分量差异」

很多做价格行为的老手盯着形态和指标,却忘了市场是按时区呼吸的。同一个成交量 spike,放在亚洲时段和欧洲时段,对价格的推动效率完全不是一个量级。 把回测数据摊开看会更直白:亚洲盘(0–8点)我给的 session_coef 是 0.7,欧洲盘(8–16点)是 1.0,美盘(16–24点)是 0.9。也就是说,亚洲时段里小幅放量就显眼,欧洲区得有更明显的偏差才够触发有效信号。 新时段开始前 30–40 分钟,指标常常走得很‘别扭’,像是市场在给新玩家腾位置。把时段系数叠到体量曲线上,原本模糊的形态会突然清晰——同样一手单,权重随时段漂移。 下面这段是用 pandas 给 K 线打时段系数的核心逻辑,直接能抄去验证:

MQL5 / C++
<span class="comment"># See how easy it is to take into account the impact of sessions</span>
hour = df.index.hour
<span class="comment"># Asian session is the calmest</span>
asian_mask = (hour &gt;= <span class="number">class="num">0</span>) &amp; (hour &lt; <span class="number">class="num">8</span>)
df.loc[asian_mask, <span class="class="type">class="kw">string">&class="macro">#x27;session_coef&class="macro">#x27;</span>] = <span class="number">class="num">0.7</span>&nbsp;&nbsp;&nbsp;&nbsp;
<span class="comment"># Europe - peak activity</span>
european_mask = (hour &gt;= <span class="number">class="num">8</span>) &amp; (hour &lt; <span class="number">class="num">16</span>)
df.loc[european_mask, <span class="class="type">class="kw">string">&class="macro">#x27;session_coef&class="macro">#x27;</span>] = <span class="number">class="num">1.0</span>&nbsp;&nbsp;
<span class="comment"># America - still active, but not as strong</span>
american_mask = (hour &gt;= <span class="number">class="num">16</span>) &amp; (hour &lt; <span class="number">class="num">24</span>)
df.loc[american_mask, <span class="class="type">class="kw">string">&class="macro">#x27;session_coef&class="macro">#x27;</span>] = <span class="number">class="num">0.9</span>

◍ 波动率乘量价动量凑出的趋势强度尺

有个反直觉的现象:把波动率、成交量趋势和动量绝对值三者直接相乘,理论上是三股乱流搅一起,图形却意外干净。在 EURUSD 日线回测里,这个乘积在强劲走势启动前 3~5 根柱就出现清晰峰值,而不是走完才反应。 用 MinMaxScaler 把结果压到 3~9 区间后,读数有了可直接用的分层:低于 3 市场偏沉睡;3 到 6 是走势萌芽区;高于 6 趋势拿到完整强度。叠加时段系数后,信号在伦敦-纽约重叠段的误报明显少于亚盘。 外汇和贵金属杠杆高、滑点跳空频繁,这类读数只作概率参考,不能当进场保证。打开 MT5 用自定义指标算这三个分量乘积累加,先把阈值 3/6 标成水平线,观察你常做的品种是否也提前反应。

MQL5 / C++
def _calculate_components(self, df: pd.DataFrame) -> pd.DataFrame:
    # Here it is, that very "error" - the multiplication of three components
    df[&class="macro">#x27;trend_force&class="macro">#x27;] = df[&class="macro">#x27;volatility&class="macro">#x27;] * df[&class="macro">#x27;volume_trend&class="macro">#x27;] * abs(df[&class="macro">#x27;momentum&class="macro">#x27;])
    
    # Normalize the result to a range of class="num">3 to class="num">9
    df[&class="macro">#x27;trend_force_norm&class="macro">#x27;] = self.scaler.fit_transform(
        df[&class="macro">#x27;trend_force&class="macro">#x27;].values.reshape(-class="num">1, class="num">1)
    ).flatten()
    
    # Final adjustments considering sessions 
    df[&class="macro">#x27;trend_force_adjusted&class="macro">#x27;] = df[&class="macro">#x27;trend_force_norm&class="macro">#x27;] * df[&class="macro">#x27;session_coef&class="macro">#x27;]

强弱失稳叠加放量才认逆转

在归一化后的强弱指标里,趋势强度突破等级 6 但方向开始乱抖,往往比单纯看回落更早露出反转苗头。作者在 H1 和 H4 上重测了两周,这两个周期的信号最干净,市场节奏像更讲理一些。 真正的过滤条件在体量:当上述形态出现时,若成交量超过 20 周期均量的 1.5 倍,反转概率明显抬升;量能跟不上,多半只是横盘整理。外汇与贵金属波动大、滑点凶,这种信号只作概率参考,实盘必须带止损。 误报难免,所以得用分拣逻辑把“强确认”和“弱确认”分开看。下面的片段就是方向判定与量能确认的核心计算,直接能丢进 Python 回测框架里跑。

MQL5 / C++
# Determining the trend direction
df[&class="macro">#x27;trend_direction&class="macro">#x27;] = np.sign(df[&class="macro">#x27;momentum&class="macro">#x27;])
# This is where the magic begins
df[&class="macro">#x27;direction_strength&class="macro">#x27;] = df[&class="macro">#x27;trend_direction&class="macro">#x27;] * df[&class="macro">#x27;trend_force_adjusted&class="macro">#x27;]
# Looking for reversal patterns
df[&class="macro">#x27;reversal_pattern&class="macro">#x27;] = np.where(
    (df[&class="macro">#x27;trend_force_adjusted&class="macro">#x27;] > class="num">6) &  # Strong trend(df[&class="macro">#x27;direction_strength&class="macro">#x27;].diff().rolling(class="num">3).std() > class="num">1.5),  # Directional instability
    class="num">1, class="num">0
)
df[&class="macro">#x27;volume_confirmation&class="macro">#x27;] = np.where(
    (df[&class="macro">#x27;reversal_pattern&class="macro">#x27;] == class="num">1) &
    (df[&class="macro">#x27;volume_trend&class="macro">#x27;] > df[&class="macro">#x27;volume_trend&class="macro">#x27;].rolling(class="num">20).mean() * class="num">1.5),
    &class="macro">#x27;Strong&class="macro">#x27;,
    &class="macro">#x27;Weak&class="macro">#x27;
)

「把信号画成能悬停看懂的图」

指标算完若只丢给交易者一张表或一串方程,多数人根本无从下手。作者试过纯 MatPlotLib,找信号极其反人类,前后换过十几种绘图库,最终落在 Plotly 上,核心卖点就是交互:光标移到某根蜡烛,趋势推力、成交量、方向立刻浮层显示。 配色单独耗了一周,只为降低视觉疲劳;趋势方向被做成柱线——高度是强度、颜色分多空。有交易员朋友盯着图点了五分钟,冒出一句“新手也能看清市场往哪走”。真实反馈里,有人终于不被信号绕晕,有人省下整天盯屏的工时。 下面这段 Python(Plotly)是三级图层的骨架:主图 K 线、中图趋势推力线、底图方向柱。外汇与贵金属波动剧烈、杠杆高风险,这类图只辅助读势,不替代仓位纪律。 悬停模板里 Force 保留两位小数,底图用 numpy 按方向染绿红,row_heights 把主图占到 50% 视高,符合“价格为主、指标为辅”的盯盘习惯。你可直接把 df 换成 MT5 导出的分钟级数据跑一遍,验证柱线方向是否和肉眼趋势一致。

MQL5 / C++
def create_visualization(self, df: pd.DataFrame = None):
    if df is None:
        df = self.analyze_market()
    
    fig = make_subplots(rows=class="num">3, cols=class="num">1, 
                        shared_xaxes=True,
                        subplot_titles=(&class="macro">#x27;Price&class="macro">#x27;, &class="macro">#x27;Trend Force&class="macro">#x27;, &class="macro">#x27;Trend Direction&class="macro">#x27;),
                        row_heights=[class="num">0.5, class="num">0.25, class="num">0.25],
                        vertical_spacing=class="num">0.05)
    # Main chart - candles with signal highlighting
    fig.add_trace(
        go.Candlestick(
            x=df[&class="macro">#x27;time&class="macro">#x27;],
            open=df[&class="macro">#x27;open&class="macro">#x27;],
            high=df[&class="macro">#x27;high&class="macro">#x27;],
            low=df[&class="macro">#x27;low&class="macro">#x27;],
            close=df[&class="macro">#x27;close&class="macro">#x27;],
            name=&class="macro">#x27;OHLC&class="macro">#x27;,
            hoverlabel=dict(
                bgcolor=&class="macro">#x27;white&class="macro">#x27;,
                font=dict(size=class="num">12)
            )
        ),
        row=class="num">1, col=class="num">1
    )
fig.add_trace(
        go.Scatter(
            x=df[&class="macro">#x27;time&class="macro">#x27;],
            y=df[&class="macro">#x27;trend_force_adjusted&class="macro">#x27;],
            mode=&class="macro">#x27;lines&class="macro">#x27;,
            line=dict(class="type">class="kw">color=&class="macro">#x27;blue&class="macro">#x27;, width=class="num">2),
            name=&class="macro">#x27;Trend Force&class="macro">#x27;,
            hovertemplate="<br>".join([
                "Time: %{x}",
                "Force: %{y:.2f}",
                "<extra></extra>"
            ])
        ),
        row=class="num">2, col=class="num">1
    )
fig.add_trace(
        go.Bar(
            x=df[&class="macro">#x27;time&class="macro">#x27;],
            y=df[&class="macro">#x27;trend_direction&class="macro">#x27;] * df[&class="macro">#x27;trend_force_adjusted&class="macro">#x27;],
            name=&class="macro">#x27;Trend Direction&class="macro">#x27;,
            marker_color=np.where(df[&class="macro">#x27;trend_direction&class="macro">#x27;] > class="num">0, &class="macro">#x27;green&class="macro">#x27;, &class="macro">#x27;red&class="macro">#x27;),
        ),
        row=class="num">3, col=class="num">1
    )

常见问题

把三根投影当成桥:同向拉长且高度递增是趋势增强;某根突然塌陷而另两根未跟,就是力道断桥,倾向回摆。
要分。亚盘量轻时柱线高度天然偏低,拿欧美盘的绝对值去比会误判;按各时段中位数做相对刻度才靠谱。
可以。小布盯盘的AIGC已内置多投影诊断,打开对应品种页即可悬停看每根柱线的量价动量分解,不用自己写图。
没有固定阈值,看自身滚动分位:强度尺掉出近20日下轨且伴随放量,才认作失稳叠加逆转概率上升。
不能。原文强调强弱失稳必须叠加放量才认逆转,无量走弱多是休整,等放量确认再动,外汇贵金属高风险勿抢跑。