English Русский 中文 Español Deutsch Português
preview
3Dバーによるトレンド強度・方向指標

3Dバーによるトレンド強度・方向指標

MetaTrader 5トレーディング |
148 25
Yevgeniy Koshtenko
Yevgeniy Koshtenko

はじめに

普通のローソク足には、もう新しい発見はないと思われるかもしれません。すべてがすでに発見され、数えられ、デジタル化されていると。しかし、市場を別の角度から見ると、全く予想外の姿を見せてくれるのです。

チャートを平面の絵としてではなく、生きて呼吸する有機体として想像してみてください。各バーは、ただの髭のある長方形ではなく、市場の心拍とともに脈打つ立体的な構造なのです。こうして、3Dバーのアイデアが生まれました。最初は単なる可視化の実験にすぎませんでした。なじみのあるデータを別の角度から見てみたい、という思いからでした。しかし、研究を深めるにつれて、驚くべきパターンが次々と現れました。

初めて「黄色のクラスタ」を目にした瞬間を今でも覚えています。3Dチャート上で、それは文字通り光り輝き、トレンドの反転を予兆していました。最初は偶然だと思いました。しかし、そのパターンは何度も繰り返し現れ、未来の価格変動を驚くほど正確に示してくれたのです。6か月にわたる継続的な研究、何百回もの徹夜、そして何千行ものコードのすべてが、徐々に一貫した数学モデルとして形になっていきました。

今、テスト結果を振り返ると、私たちは本当に重要な発見にたどり着いたと感じています。これは、市場の本質、その構造の奥深くに存在するものです。従来のテクニカル分析では太刀打ちできません。これらのパターンは、テンソル分析の視点を通して、チャートの平面を超えて3次元の世界に立ち上がることで初めて見えてくるのです。

この記事では、私の発見を共有したいと思います。日常的な市場データを新しい角度から観察することで、まだポジションを取る時間がある段階で、トレンドの強さや方向について驚くほど正確なシグナルを得る方法をお見せしたいのです。シートベルトを締めてください。私たちは、3D市場の旅へと出発しようとしています。


基本的な市場状態テンソルの構造

子どものころ、ルービックキューブをどうやって解いていたか覚えていますか。最初は完全なカオスに見えます。しかし、原理を理解すると、すべての面がひとつの絵を形作るようになります。市場もまったく同じです。私はデータを3次元構造、つまりテンソルにまとめ始めました。難しそうに聞こえますが、本質的には価格、出来高、時間が互いにどう影響し合っているかを可視化する方法にすぎません。

最初の実験はあまり印象的ではありませんでした。数学は美しい方程式を作ろうとせず、この終わりのない数字の流れに苛立ちさえ感じました。そして…そのとき、私はただ数字としてではなく考えるのをやめたのです。

各ローソク足を、単なる始値・高値・安値・終値のセットではなく、生きた有機体として想像してみてください。身体の質量のような出来高があります。運動のようなインパルスがあります。そしてDNAのような内部構造があります。この角度からデータを見るようになったとき、すべてがつながり始めました。

その結果、次のような「キューブ」ができました。

  • 1面は従来の価格データ
  • 2つ目の面は出来高ですが、単なる取引数ではなく、その内部構造も含みます
  • 3つ目の面は、長らく捉えられなかった時間のループです

最も驚いたのは、このモデルの最初のテストを開始したときです。チャートが文字通り命を持ったかのように見えました。以前はただの線に過ぎなかったところに、明確な立体構造が現れたのです。そしてそれは動いていました。まるで市場の内部リズムに合わせて脈打っているかのようでした。

しかし何より重要なのは、この構造が奇妙なパターンを示し始めたことです。最初は、ただの可視化のアーティファクトだと思っていました。しかし、より多くのデータをモデルに通すほど、パターンは明確になっていきました。これらのパターンは、強い価格変動の直前に現れました。まるで市場が、自分の意図を警告しているかのようです。

次に、このデータをどのように共通の基準にまとめたかをお話しします。これは別の物語で、ガンの古い著作での偶然の発見から始まったのです。


ガン方式によるデータの正規化

私はガンの著作にまったく偶然出会いました。全く別の資料を探してアーカイブされたPDFをめくっていたとき、突然、奇妙なグラフが目に入りました。正方形、角度、いくつかの螺旋……最初の印象は「また市場の神秘家の話か」と思いました。しかし、何かに導かれるように、もっと深く調べることにしました。そして、ガンの手法について記事を書いたのです。

するとわかったことがあります。幾何学的な装飾の下には、驚くほど洗練されたデータ正規化のアイデアが隠れていたのです。ガンは直感的に、私が数学的に到達しようとしていた「市場のスケール不変性の原理」を理解していたのです。

私は彼のノートを3週間かけて読み込みました。その半分は完全なオカルトとして捨てざるを得ませんでした。しかし残りはといえば、本物の何かが確かにありました。特に、彼の時間ループへのアプローチには感銘を受けました。夜中にベッドから飛び起き、コンピュータに向かって突然のひらめきを確認したことを覚えています。

正しく時間間隔をスケーリングすると、市場がほぼ結晶のような構造を示し始めることがわかりました。それはまるで顕微鏡で雪の結晶を見るような感覚です。ズームするたびに同じパターンが現れますが、サイズだけが変わるのです。

私は彼の基本原理を自分のモデルに合わせて再構築しました。ガンの「魔法の数字」を使う代わりに、ボラティリティに基づいて計算される動的な比率を使用しました。各テンソルパラメータはもはや固定スケールに正規化されるのではなく、現在の市場の状態に応じて自動調整される「浮動レンジ」に正規化されるようにしたのです。

それはまるで楽器を調律しているような感覚でした。弦楽器がようやく一つの音で響き始めるときのあの感覚がお分かりでしょうか。私も最初の結果を見たとき、ほぼ同じ気持ちになりました。チャートはもはや個別の要素に分解されることはなく、ひとつのものとして息づき始めたのです。

最も困難だったのは、正規化の感度とモデルの堅牢性のバランスを見つけることでした。感度を細かくしすぎると、市場のノイズに反応してしまいます。逆に粗すぎると、重要なシグナルが失われます。私はこのパラメータを2週間かけて調整し、黄金比を見つけました。

しかし、本当のブレイクスルーは、この正規化をトレンドの出来高成分に適用したときに起こりました。そして、最も面白いことが始まったのです。


トレンドの出来高コンポーネントの計算

ここからが本番です。データを正規化した後、予期せぬ問題に直面しました。従来の出来高指標では、トレンド転換の重要な瞬間を「見逃してしまう」のです。OBVやMFIを改良しようと1週間試行錯誤したことを覚えています。結果は平凡なものでした。

そして、古い指標のソースコードを調べていたとき(作者はもはや見つかりません)、出来高プロファイルを計算する面白いアプローチに出会いました。そのアイデアは、あまりにもシンプルでありながら天才的でした。絶対値としての出来高を見るのではなく、移動平均との関係で見る、という方法です。以下がそのコードです。

def _calculate_components(self, df: pd.DataFrame) -> pd.DataFrame:
    # Basic components
    df['volatility'] = df['close'].pct_change().rolling(20).std()
    df['momentum'] = df['close'].pct_change(5)
    
    # Here it is, the key place - the volumetric profile
    df['volume_ma'] = df['tick_volume'].rolling(20).mean()
    df['volume_trend'] = df['tick_volume'] / df['volume_ma']
    
    # Trend strength as a derivative of three components
    df['trend_force'] = df['volatility'] * df['volume_trend'] * abs(df['momentum'])

ここで何が起こっているか見てください。ここで注目すべきは、単に出来高を合計するのではなく、「出来高の加速度」のようなものを作っている点です。出来高が平均に対して急増するとき、それが最初の警告サインになります。しかし、面白いのは、ここにボラティリティとモメンタムを組み合わせたときです。

私は移動平均の期間をさまざまに試しました。10、15、25...最終的に、20バーが感度とシグナルの安定性のバランスとして最適であることがわかりました。 

そして、本当の「驚き」は、取引セッションの比率を加えたときに起こりました。

# Activity ratios of different sessions
df['session_coef'] = 1.0
hour = df.index.hour
        
df.loc[(hour >= 0) & (hour < 8), 'session_coef'] = 0.7    # Asian
df.loc[(hour >= 8) & (hour < 16), 'session_coef'] = 1.0   # European
df.loc[(hour >= 16) & (hour < 24), 'session_coef'] = 0.9  # American

チャートが文字通り命を持ったかのように見えました。これにより、出来高の急増が単に絶対値としてではなく、現在の取引セッションの文脈で評価されるようになったのです。まるで潮の満ち引きのようです。各セッションにはそれぞれの特性があり、それぞれ独自の「引力の力」を持っているのです。

そして最も重要なことに、この計算式は私が「前触れ」と呼んだ現象を示すようになりました。強い値動きの数本前に、出来高プロファイルが特徴的なパターンを形成し始めるのです。まるで市場がジャンプする前に「息を整えている」かのようです。

次の課題は、これをどう正しく可視化するか、ということでした。


3次元空間における価格動向

この混沌を正しく可視化する方法を見つけるのに、かなりの時間がかかりました。MatPlotLibで3Dチャートを作ろうとした最初の試みは、取引に役立つというより、むしろクリスマスツリーのように見えました。Plotlyもすぐにはうまくいきませんでした。チャートは情報が多すぎたり、重要な詳細が欠けていたりしました。

そんなとき、偶然が助けてくれました。娘と一緒に組み立てブロックで橋のようなものを作って遊んでいたとき、ふと気づいたのです。「派手な3Dグラフィックは全部必要ない。投影を正しく並べるだけで十分だ」と。こうして私は次のような構造を考えました。

def create_visualization(self, df: pd.DataFrame = None):
    if df is None:
        df = self.analyze_market()
    
    df = df.reset_index()
            
    # Three projections of our "bridge"
    fig = make_subplots(rows=3, cols=1, 
                       shared_xaxes=True,
                       subplot_titles=('Price', 'Trend Force', 'Trend Direction'),
                       row_heights=[0.5, 0.25, 0.25],
                       vertical_spacing=0.05)

    # Main chart - classic candles
    fig.add_trace(
        go.Candlestick(
            x=df['time'],
            open=df['open'],
            high=df['high'],
            low=df['low'],
            close=df['close'],
            name='OHLC'
        ),
        row=1, col=1
    )

同じ空間の3つの投影をとるイメージです。上のウィンドウには、いつものローソク足チャートを表示します。しかし、これは氷山の一角に過ぎません。本当に面白くなるのは2番目のウィンドウです。

# The trend force is our main feature
    fig.add_trace(
        go.Scatter(
            x=df['time'],
            y=df['trend_force_adjusted'],
            mode='lines',
            line=dict(color='blue', width=2),
            name='Trend Force'
        ),
        row=2, col=1
    )

    # Reference levels
    fig.add_hline(y=3, line_dash="dash", line_color="yellow", row=2, col=1)
    fig.add_hline(y=6, line_dash="dash", line_color="green", row=2, col=1)

ここでは、トレンドの強さを示しています。私はこれらの基準レベル(3と6)を経験的に見つけました。トレンド強度がレベル6を突破すると、ほぼ確実に強い値動きを意味します。レベル3は乱気流ゾーンのようなもので、トレンドが強まるか反転するかの分岐点になります。

そして、最も魔法のような現象が起こるのは、下のウィンドウです。

# Trend direction as a derivative of force
    fig.add_trace(
        go.Bar(
            x=df['time'],
            y=df['trend_direction'] * df['trend_force_adjusted'],
            name='Trend Direction',
            marker_color=np.where(df['trend_direction'] > 0, 'green', 'red')
        ),
        row=3, col=1
    )

ここでは、単にトレンドの方向を見るだけでなく、その強さの変化も動的に把握できます。緑色のバーが高いトレンド強度の値に対して上昇しているときは強い買いシグナルです。逆に、赤色のバーが強度6以上の水準で上昇しているときは確実な売りシグナルとなります。

しかし、現実に戻りましょう。可視化を終えた後、次に問題となったのは時間ループの扱いでした。


時間コンポーネントと取引セッション

古典的なテクニカル分析で、いつも不思議に思っていたことがあります。それは、誰もが時間のことを簡単に忘れてしまうということです。トレーダーはチャートを見て指標を計算しますが、単純な事実を見逃しています。市場は異なるタイムゾーンで動いているのです。

最初の警告は、奇妙なパターンに気づいたときに鳴りました。私のシグナルは、ヨーロッパセッションの間にずっとうまく機能していたのです。最初は偶然だと思って無視しました。しかし、さらに深く調べてみると、次のことがわかりました。

# See how easy it is to take into account the impact of sessions
hour = df.index.hour

# Asian session is the calmest
asian_mask = (hour >= 0) & (hour < 8)
df.loc[asian_mask, 'session_coef'] = 0.7    

# Europe - peak activity
european_mask = (hour >= 8) & (hour < 16)
df.loc[european_mask, 'session_coef'] = 1.0  

# America - still active, but not as strong
american_mask = (hour >= 16) & (hour < 24)
df.loc[american_mask, 'session_coef'] = 0.9

私はこれらの比率をほぼその場で思いつき、さまざまなオプションをテストしていました。夜通しで異なる値を使ってバックテストを走らせていたことを覚えています。モニターから目を離すことができないほど、結果がワクワクするものでした。

そして最も面白いことが起こったのは、これらの比率を出来高プロファイルに重ねたときです。突然、すべてがつながりました。同じ出来高でも、取引セッションによってまったく異なる「重み」を持つことがわかったのです。

  • アジアセッションでは、わずかな出来高の急増でも重要な意味を持つ
  • ヨーロッパセッションでは、より大きな変動が必要
  • セッションの境目では、非常に興味深い現象が起こる

しかし、本当のブレイクスルーは、さらに別の要素である「セッション間の移行」を加えたときに起こりました。新しいセッションが始まる30~40分前に、指標の動きが…奇妙になり始めるのです。まるで市場が、新たな参加者の到来に備えているかのようでした。そして、ここからが…


トレンド強度の積分指標

時には、最も重要な発見は不運なミスから生まれることがあります。私の場合、それはコードのバグから始まりました。誤って変数を掛け合わせてしまった結果、グラフにとんでもない異常が表示されたのです。最初の衝動はすべて書き直すことでした。しかし、何かに導かれるように、じっくりとその異常を観察してみました。

すると、偶然にも後に「積分指標」と呼ぶことになるものを作り出していたことがわかりました。見てみましょう。

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

ここで何が起こっているかというと、ボラティリティに出来高トレンド、そしてモメンタムの絶対値を掛け合わせています。理論上は、完全な混沌が生じるはずです。しかし、実際には、驚くほど明確なトレンド強度の指標になったのです。

過去データでこの式をテストしたときの驚きを今でも覚えています。チャートは、強い値動きが始まる正確なポイントで明確なピークを示していました。事後的ではなく、動きが始まる数本前のバーでです。

さらに面白くなったのは、MinMaxScalerによる正規化を加えたときです。範囲はほとんどランダムに3から9に設定しました。ただチャートが見やすくなるように思えたからです。すると驚いたことに、この数値が意思決定のためのほぼ完璧なレベルを作り出していることに気づきました。

  • 3未満:市場は「眠っている」状態
  • 3から6:値動きが始まる
  • 6以上:トレンドが完全な強さを獲得

さらに、これにセッション比率を加えると、本当に驚きました。シグナルが非常に明確になり、いつも懐疑的な隣のトレーダーですら、バックテストを見て口笛を吹くほどでした。

しかし、ここでの最大の発見はまだ先でした。この指標は単にトレンドの強さを測るだけでなく、反転を予測できる可能性があることに気づいたのです。


将来の値動きの方向を決定する

積分指標を発見した後、私は文字通り反転パターンの探索に取り憑かれてしまいました。数週間、モニターの前に座り、チャートを前後に動かし続けました。妻もさすがに心配し始めました。面白い発見をすると、食事を忘れることさえありました。

そしてある夜(なぜ重要な発見はいつも夜に起こるのでしょうか?)、私は奇妙なパターンに気づきました。強いトレンド反転の前に、強度指標は、落ちるのではなく、特別な方法で振動し始めたのです。

# Determining the trend direction
df['trend_direction'] = np.sign(df['momentum'])

# This is where the magic begins
df['direction_strength'] = df['trend_direction'] * df['trend_force_adjusted']

# Looking for reversal patterns
df['reversal_pattern'] = np.where(
    (df['trend_force_adjusted'] > 6) &  # Strong trend
    (df['direction_strength'].diff().rolling(3).std() > 1.5),  # Directional instability
    1, 0
)

具体的には、トレンド強度が6を超え(先ほどの正規化を思い出してください)、かつ方向性が不安定になると、ほぼ確実に反転を予告することがわかりました。

この観察を、異なる時間軸や銘柄で再テストするのに2週間を費やしました。どこでも機能しましたが、特にH1とH4の時間足で結果が明確でした。まるで、この時間軸では市場が最も理性的に「考えている」かのように見えました。

しかし、真の洞察は、ここに出来高プロファイルを重ねたときに生まれました。

df['volume_confirmation'] = np.where(
    (df['reversal_pattern'] == 1) &
    (df['volume_trend'] > df['volume_trend'].rolling(20).mean() * 1.5),
    'Strong',
    'Weak'
)

すべてがつながったのです。すべての反転が同じではないことがわかりました。反転パターンと平均を上回る強い出来高の増加が重なると、ほぼ確実な反転となります。一方で、出来高がそれを支えない場合は、ほとんどの場合、単なる調整に過ぎません。

私はこの結果を以前の統計の先生に見せました。先生は方程式を長い間見つめ、私を見上げて尋ねました。「2020年前のデータで確認しましたか?」私はうなずきました。「ではその後は?」私は再びうなずきました。「ふむ…なるほど。ランダムウォークとは矛盾しますが…確かに真実のように見えますね。」

もちろん、いくつかの偽陽性もありました。しかし、このための仕分けシステムはすでに用意してありました。


チャート上でのシグナルの可視化

指標のすべての要素が整ったとき、次の疑問が生まれました。それは「これをトレーダーにどう見せるか」です。すべての人が方程式や表を理解できるわけではありません。この問題にはかなり苦労しましたが、最終的に「3段階の可視化」というアイデアを思いつきました。

最初の試みは、純粋なMatPlotLibでおこないましたが、うまくいきませんでした。シグナルを探すのは非常に骨の折れる作業でした。約10種類のライブラリを試した後、最終的にPlotlyに落ち着きました。そして、最終的にできあがったのが次のような構造です。

def create_visualization(self, df: pd.DataFrame = None):
    if df is None:
        df = self.analyze_market()
    
    fig = make_subplots(rows=3, cols=1, 
                       shared_xaxes=True,
                       subplot_titles=('Price', 'Trend Force', 'Trend Direction'),
                       row_heights=[0.5, 0.25, 0.25],
                       vertical_spacing=0.05)

    # Main chart - candles with signal highlighting
    fig.add_trace(
        go.Candlestick(
            x=df['time'],
            open=df['open'],
            high=df['high'],
            low=df['low'],
            close=df['close'],
            name='OHLC',
            hoverlabel=dict(
                bgcolor='white',
                font=dict(size=12)
            )
        ),
        row=1, col=1
    )

最も重要な特徴はインタラクティブ性です。ローソク足の上にカーソルを置くだけで、トレンド強度、出来高、方向などのすべてのパラメータを即座に確認できます。そして配色も重要でした。目に優しい色合いを選ぶのに、私は1週間かけました。

fig.add_trace(
        go.Scatter(
            x=df['time'],
            y=df['trend_force_adjusted'],
            mode='lines',
            line=dict(color='blue', width=2),
            name='Trend Force',
            hovertemplate="<br>".join([
                "Time: %{x}",
                "Force: %{y:.2f}",
                "<extra></extra>"
            ])
        ),
        row=2, col=1
    )

トレンド方向の可視化は別の話です。バーの形にして、バーの高さが強さを示し、色が方向を示すようにしました。

fig.add_trace(
        go.Bar(
            x=df['time'],
            y=df['trend_direction'] * df['trend_force_adjusted'],
            name='Trend Direction',
            marker_color=np.where(df['trend_direction'] > 0, 'green', 'red'),
        ),
        row=3, col=1
    )

完成版をトレーダーの友人に見せたときの反応を今でも覚えています。彼はチャートを黙って約5分間クリックして眺めた後、「これなら、初心者でも市場の方向がすぐわかるね」と言ったのです。

しかし、本当にうれしかったのは、実際のユーザーからレビューが届き始めたときです。ある人は「シグナルで迷わなくなった」と書き、また別の人は「長時間モニターの前に座らなくても取引できるようになった」と感謝してくれました。


MetaTrader 5でトレンド強度指標を実装してみる

Python版の指標で成功を収めた後、自然に次の疑問が生まれました。それは「これをMetaTrader 5にどう移植するか」です。

この課題は、興味深いものでした。MQL5はもちろん強力な言語ですが、pandasやnumpyがないため、多くの工夫が必要でした。こうして私は次のような構造を考えました。

//+------------------------------------------------------------------+
//|                                          TrendForceIndicator.mq5 |
//+------------------------------------------------------------------+
#property copyright "Your Name"
#property link      "https://www.mql5.com"
#property version   "1.00"
#property indicator_separate_window
#property indicator_buffers 3
#property indicator_plots   3

// Rendering buffers
double TrendForceBuffer[];
double DirectionBuffer[];
double SignalBuffer[];

// Inputs
input int    InpMAPeriod = 20;     // Smoothing period
input int    InpMomentumPeriod = 5; // Momentum period
input double InpSignalLevel = 6.0;  // Signal level

//+------------------------------------------------------------------+
//| Custom indicator initialization function                         |
//+------------------------------------------------------------------+
int OnInit()
{
    // Setting the indicator
    SetIndexBuffer(0, TrendForceBuffer, INDICATOR_DATA);
    SetIndexBuffer(1, DirectionBuffer, INDICATOR_DATA);
    SetIndexBuffer(2, SignalBuffer, INDICATOR_DATA);
    
    // Rendering styles
    PlotIndexSetString(0, PLOT_LABEL, "Trend Force");
    PlotIndexSetInteger(0, PLOT_DRAW_TYPE, DRAW_LINE);
    PlotIndexSetInteger(0, PLOT_LINE_COLOR, clrBlue);
    
    PlotIndexSetString(1, PLOT_LABEL, "Direction");
    PlotIndexSetInteger(1, PLOT_DRAW_TYPE, DRAW_HISTOGRAM);
    
    PlotIndexSetString(2, PLOT_LABEL, "Signal");
    PlotIndexSetInteger(2, PLOT_DRAW_TYPE, DRAW_LINE);
    PlotIndexSetInteger(2, PLOT_LINE_COLOR, clrRed);
    
    return(INIT_SUCCEEDED);
}

//+------------------------------------------------------------------+
//| Custom indicator iteration function                              |
//+------------------------------------------------------------------+
int OnCalculate(const int rates_total,
                const int prev_calculated,
                const datetime &time[],
                const double &open[],
                const double &high[],
                const double &low[],
                const double &close[],
                const long &tick_volume[],
                const long &volume[],
                const int &spread[])
{
    // Check for data sufficiency
    if(rates_total < InpMAPeriod) return(0);
    
    // Calculation of components
    int start = (prev_calculated > 0) ? prev_calculated - 1 : 0;
    
    for(int i = start; i < rates_total; i++)
    {
        // Volatility
        double volatility = 0.0;
        if(i >= InpMAPeriod)
        {
            double sum = 0.0;
            for(int j = 0; j < InpMAPeriod; j++)
            {
                double change = (close[i-j] - close[i-j-1]) / close[i-j-1];
                sum += change * change;
            }
            volatility = MathSqrt(sum / InpMAPeriod);
        }
        
        // Momentum
        double momentum = 0.0;
        if(i >= InpMomentumPeriod)
        {
            momentum = (close[i] - close[i-InpMomentumPeriod]) / close[i-InpMomentumPeriod];
        }
        
        // Volume trend
        double volume_ma = 0.0;
        if(i >= InpMAPeriod)
        {
            for(int j = 0; j < InpMAPeriod; j++)
            {
                volume_ma += tick_volume[i-j];
            }
            volume_ma /= InpMAPeriod;
        }
        
        double volume_trend = volume_ma != 0 ? (double)tick_volume[i] / volume_ma : 0;
        
        // Session ratio
        MqlDateTime dt;
        TimeToStruct(time[i], dt);
        double session_coef = GetSessionCoefficient(dt.hour);
        
        // Trend strength calculation
        TrendForceBuffer[i] = NormalizeTrendForce(volatility * MathAbs(momentum) * volume_trend) * session_coef;
        DirectionBuffer[i] = momentum > 0 ? TrendForceBuffer[i] : -TrendForceBuffer[i];
        
        // Signal line
        SignalBuffer[i] = InpSignalLevel;
    }
    
    return(rates_total);
}

//+------------------------------------------------------------------+
//| Get session ratio                                                |
//+------------------------------------------------------------------+
double GetSessionCoefficient(int hour)
{
    if(hour >= 0 && hour < 8)   return 0.7;  // Asian session
    if(hour >= 8 && hour < 16)  return 1.0;  // European session
    if(hour >= 16 && hour < 24) return 0.9;  // American session
    return 1.0;
}

//+------------------------------------------------------------------+
//| Normalization of the trend strength indicator                    |
//+------------------------------------------------------------------+
double NormalizeTrendForce(double force)
{
    // Simple normalization to range [3, 9]
    double max_force = 0.01;  // Selected empirically
    return 3.0 + 6.0 * (MathMin(force, max_force) / max_force);
}

最も難しかったのは、pandasのローリングウィンドウの挙動を再現することでした。MQL5では、すべてをループで計算する必要があります。しかし、その分パフォーマンスは向上し、指標はPython版よりも明らかに高速に動作します。


結論

この長い研究の最後に、いくつか重要な観察を共有したいと思います。PythonからMQL5へのアルゴリズム移植は、予期せぬ最適化の機会をもたらしました。最初は不利だと思っていた点(慣れ親しんだライブラリが使えないこと)が、むしろ利点となり、コードはより高速かつ効率的になったのです。

最も難しかったのは、シグナルの精度と指標の処理速度のバランスを見つけることでした。追加のパラメータや新しいチェックはすべて、システムへの負荷となります。最終的には、指標がほぼリアルタイムでティックデータを処理しつつ、高いシグナル精度を維持できる最適な比率を実現できました。

実際のトレーダーの反応も特筆すべきです。このプロジェクトを始めたとき、主な目的は自分自身のために新しく面白いものを作ることでした。しかし、他のトレーダーがこの指標を使い始めると、私たちが本当に重要な発見をしたことが明らかになりました。特に、経験豊富なトレーダーから「この指標のおかげで市場を新しい見方で見ることができる」とフィードバックをもらったときは非常に嬉しかったです。

もちろん、これは万能の聖杯ではありません。他のテクニカル分析ツールと同様に、この指標も理解と適切な運用が必要です。しかし、最大の利点は、従来の分析では見えない市場の動態の側面を可視化できることです。

まだやるべきことは多くあります。適応型のパラメータ設定を追加したり、シグナルの仕分けシステムを改良したり、場合によっては機械学習の要素を組み込むことも検討しています。しかし、今こうして結果を見返すと、この道を進んだ価値があったと確信しています。

何よりも、テクニカル分析のように一見十分に研究され尽くした分野でも、常に革新の余地があることを確信しました。慣れ親しんだものを新しい角度から見ることを恐れず、予期せぬ発見に備えることが大切なのです。

MetaQuotes Ltdによってロシア語から翻訳されました。
元の記事: https://www.mql5.com/ru/articles/16719

添付されたファイル |
最後のコメント | ディスカッションに移動 (25)
Bogard_11
Bogard_11 | 11 1月 2025 において 17:10
Vitaly Muzichenko #:

しかし、それはあなたや他のトレーディングの達人たちに大いに役立つ。

ここでエントリーし、ここでエグジットし、各トレードから100500ppを稼ぎます。

すべてのコーダーの問題は何か知っていますか?彼らはパターンの探し方を忘れている。

彼らはすべて同じパターンに従っている - 今、我々はコードにアイデアを入れて、マシンが聖杯を見つけるでしょう!というように、100500回ぐるぐる回る。))

しかし、それを手で取ってチェックしたり、電卓で計算したりするのは......。いや、穴を開けるのは名誉なことではない...。))

入射角と反射角が等しいという、通常の物理法則に従っていることを理解していないわけだ。グラフシステムは100年前に説明された。その通りに機能する。しかし、それを理解するためには、数学的なつながりをすべて見つけるために、歴史との取り組みにそれなりの時間を費やす必要がある。そして特に思考を整理し直すことによってね。

最初のインパルスでさえ、将来の反転レベルに関する情報を含んでいる。ポンドの誤差は通常2-5ピップス以下です。

Vitaly Muzichenko
Vitaly Muzichenko | 11 1月 2025 において 17:23
Bogard_11 #:

すべてのコーダーの問題点を知っているだろうか?彼らはパターンを探す方法を忘れている。

彼らはすべて同じパターンに従っている - 今、我々はコードにいくつかのアイデアを入れて、マシンが聖杯を見つけるでしょう!そうして100500回円を描く。))

しかし、それを手で取ってチェックしたり、電卓で計算したり......。いや、穴を開けるのは名誉なことではない...。))

入射角と反射角が等しいという、通常の物理法則に市場が従っていることを理解していないのはそのためだ。グラフシステムは100年前に説明された。その通りに機能する。しかし、それを理解するためには、数学的なつながりをすべて見つけるために、歴史との取り組みにそれなりの時間を費やす必要がある。そして特に、考え方を整理し直すことによって。

最初のインパルスでさえ、将来の反転レベルに関する情報を含んでいる。ポンドの誤差は通常2-5ピップス以下です。

異なるユーザーから数百の異なるシステムがコーディングされ、それらをテストし、すべての欠点と利点を確認します。

手作業でパターンを選んで見逃すことはありません。機械は何も見逃さない、それが利点です。

そして、100年前に機能していたものは、技術の進歩とともに機能しなくなりました。

ブローカーに電話や電報で取引していた時代、pipsがどのように機能していたか想像できますか?

Bogard_11
Bogard_11 | 11 1月 2025 において 18:20
Vitaly Muzichenko #:

そして100年前に機能していたものは、技術の進歩とともに機能しなくなった。

なぜうまくいかなくなったのか、詳しく説明してくれる?どこかのブローカーかマーケットメーカーが物理学と数学の法則をキャンセルしたのか?

どうしてこんなスクープを見逃したのだろう。この出来事がいつ起こったのか、そしてキャンセルしたのは誰なのか、知ることはできますか?

Bogard_11
Bogard_11 | 11 1月 2025 において 18:29
Vitaly Muzichenko #:

さまざまなユーザーによる数百種類のシステムがコーディングされ、あなたはそれらをテストし、すべての欠点と利点を確認する。

手作業で選ぶのではなく、いくつかのパターンを見逃さない。機械が何かを見逃すことはない。

何かを正しくコーディングするには、まず手作業ですべての パターンを見つけなければならない。機械は間違っていないかもしれないが、あなたが機械に入れたもの、つまり機械を使わずに自分で見つけたものしか探さない!AIの能力を狂信的に信じていることに、私は時々驚かされる!)))

あなたがABCの 文字を検索するようにコーディングしたのは、偶然見つけたか、誰かが促したかのどちらかだからだ。しかし!アルファベットには他にも文字があり、あなたはそれを知らない......。機械は何を教えてくれるのだろう?

それは市場でも同じで、一般的なルールがあり、マットがあり、モデルがあり、しかし毎日それらは異なる比率を持っている(すべてが1つの一般的なモデルに収まるが)。価格であっても、計算値から+-3 ポイントの反動はあるでしょうが、その時その時でうまくいくでしょう。あるいは、価格通りだが、数バーのズレがある。

imcapec
imcapec | 27 1月 2025 において 12:09
Bogard_11 #:

何かを正しくコーディングするためには、まず自分の手ですべての パターンを見つけなければならない。機械は間違っていないかもしれないが、あなたが機械に入れたもの、つまり機械を使わずに自分で見つけたものだけを探してくれる!AIの能力を狂信的に信じていることに、私は時々驚かされる!)))

あなたがABCの 文字を検索するようにコーディングしたのは、偶然見つけたか、誰かが促したかのどちらかだからだ。しかし!アルファベットには他にも文字があり、あなたはそれを知らない......。機械は何を教えてくれるだろう?

それは市場でも同じで、一般的なルールがあり、マットがあり、モデルがあり、しかし毎日異なる比率を持っている(すべてが1つの一般的なモデルに収まるが)。価格でも、計算値から+-3 ポイントの反動はありますが、その時その時でうまくいきます。あるいは、価格通りだが、数バーのズレがある。

数値系列は変化しませんが、コンマが変化します。

ビッグバンビッグクランチ(BBBC)アルゴリズム ビッグバンビッグクランチ(BBBC)アルゴリズム
本記事では、ビッグバンビッグクランチ(BBBC)法について紹介します。本手法は2つの主要な段階から構成されます。すなわち、ランダムな点を周期的に生成する段階と、それらを最適解へ圧縮する段階です。本アプローチは探索と精緻化を組み合わせることで、段階的により良好な解を導出し、新たな最適化の可能性を開くことが可能です。
取引におけるニューラルネットワーク:ウェーブレット変換とマルチタスクアテンションを用いたモデル 取引におけるニューラルネットワーク:ウェーブレット変換とマルチタスクアテンションを用いたモデル
ウェーブレット変換とマルチタスク自己アテンション(Self-Attention)モデルを組み合わせたフレームワークを紹介します。本フレームワークは、ボラティリティの高い市場環境における予測の応答性および精度の向上を目的としています。ウェーブレット変換により、資産収益率を高周波成分と低周波成分に分解し、長期的な市場トレンドと短期的な変動の双方を的確に捉えることが可能となります。
取引における多項式モデル 取引における多項式モデル
本記事では、直交多項式について説明します。直交多項式を活用することで、より正確で効果的な市場分析が可能になり、トレーダーはより多くの情報に基づいた意思決定をおこなうことができるようになります。
市場シミュレーション(第2回):両建て注文(II) 市場シミュレーション(第2回):両建て注文(II)
前回の記事とは異なり、今回はエキスパートアドバイザー(EA)を用いて選択オプションをテストしてみます。最終的な解決策ではありませんが、現時点では十分な内容となっています。本記事を通じて、1つの実現可能な解決方法の実装手順を理解できます。