English Русский 中文 Español Deutsch Português
preview
取引所価格のバイナリコードの分析(第1回):テクニカル分析の新たな視点

取引所価格のバイナリコードの分析(第1回):テクニカル分析の新たな視点

MetaTrader 5インディケータ |
72 2
Yevgeniy Koshtenko
Yevgeniy Koshtenko

はじめに

映画『マトリックス』でネオが世界を緑のバイナリコードとして見ていたのを覚えていますか。もし私たちも、あのようにチャートを眺めてみたらどうなるでしょうか。日々の取引の中で、私はしばしばこう考えていました。市場の動きを一種のコードや言語だと想像したら、市場が私たちに伝えようとしていることを「聞く」ことはできるのだろうか。

このアイデアは、ビットコインのチャートを分析しているときにふと生まれました。価格の急騰や急落を観察していると、いくつかの動きの組み合わせがまるで単語の中の文字のように繰り返されていることに気づいたのです。そこで、これらの動きをバイナリ形式に符号化し、その結果を読み取れないだろうかと考えたのです。

少し突飛に聞こえますか。そうかもしれません。しかし、思い出してください。テクニカル分析とは、もともとパターンを探す学問なのではないでしょうか。ローソク足もまた、価格変動を視覚的に符号化したシステムです。市場そのものが、デジタル的に符号化された膨大な情報の流れなのです。

この記事では、価格変動を意味のあるバイナリコードのシーケンスに変換するという私の試みを紹介します。単純な値動きから複雑なパターンまで、市場のさまざまな側面をどのようにバイナリ形式で表現できるのかを見ていき、最終的にはこの「市場の言語」を読み解く手がかりを探っていきます。

確かに、このアプローチは一般的ではありません。しかし、取引とは本来、他の誰も見ていない視点を探し続けることではないでしょうか。

それでは、一緒に「ウサギの穴」の奥へ潜り込み、このアイデアがどこへ導くのかを探っていきましょう。


価格変動をバイナリコードとして表現するという概念

私のアプローチは、非常にシンプルでありながら興味深い発想に基づいています。それは「どんな価格変動も、0と1のシーケンスとして表現できる」というものです。考えてみてください。チャートを見たとき、私たちは何を観察しているでしょうか。上昇と下落、強い動きと弱い動き、平均以上または以下の出来高。実際のところ、私たちは無意識のうちに、これらの情報を頭の中でバイナリ的に変換しているのです。

簡単な例を挙げてみましょう。連続する3本のローソク足があり、それぞれ「上昇」「下落」「再び上昇」と動いたとします。最も単純な表現では、これは「101」として符号化できます。しかし、ここでさらにパラメータを追加してみたらどうでしょうか。たとえば、動きの強さ、取引量、移動平均線との位置関係などを考慮することも可能です。

私の実験の結果では、上昇と下落という単純な二値化だけでも、興味深い結果を生むことが分かりました。しかし、本当の面白さは、複数の時間足を組み合わせたときに現れます。想像してみてください。1時間足で「101」と見えるシーケンスが、日足や週足ではまったく異なる形に見える可能性があります。このとき、市場データ全体がまるで多次元のデータ行列となり、それぞれの値動きが固有のバイナリ列として表現されるのです。

特に暗号資産を分析する際には、非常に興味深いパターンが現れます。ビットコインの高いボラティリティは、バイナリ表現においても明確で再現性のあるリズムを生み出します。時にはそれがモールス信号のように見えることもあります。点と線のリズムが、今度は価格の上昇と下落として現れているのです。

もちろん懐疑的な方は、これは従来のテクニカル分析を複雑化しただけだと言うかもしれません。しかし、進化とはそもそも、既知のものを新しい視点で見ることではないでしょうか。かつてローソク足も奇抜な手法と見なされていましたが、今やどのトレーダーもそれなしでは市場を語れません。

この後のセクションでは、価格変動を符号化するための具体的な手法と、それが実際の取引にどのように役立つのかを詳しく見ていきます。その前に、ぜひあなた自身のお気に入りのチャートを開いてみてください。そこに「0」と「1」の連なりを見出せるかどうか、試してみてください。きっと、テクニカル分析の見え方が少し変わるはずです。


変換の基本原則

初期のコンピューターゲームを覚えていますか。画面のグラフィックがピクセルで構成され、音が8ビットで表現されていたあの頃のゲームです。私たちの市場分析のアプローチは、それに少し似ています。複雑な相場の世界をシンプルなバイナリコードに変換する、まるでレトロゲームを作るかのような方法です。

このアイデアがどのようにして生まれたのかお話ししましょう。ある日、別のインジケーターを開発している際、誤ってチャートに通常のラインではなく、0と1だけを表示してみました。するとどうでしょう。数字の連なりの中に、面白いパターンが現れ始めたのです。まるで市場が、自分のバイナリ言語で何かを伝えようとしているかのようでした。

コード好きの方のために、Pythonでの基本的な実装例をご紹介します。

def encode_price_movement(self, prices):
    normalized = ((prices - np.min(prices)) / 
                 (np.max(prices) - np.min(prices)) * 255).astype(np.uint8)
    data = bytes(normalized)
    entropy = hashlib.sha256(data).digest()
    mnemonic = self.mnemo.to_mnemonic(entropy)
    return mnemonic

動きの方向(上昇/下落)

取引で最も面白いことは何だと思いますか。それは、私たちが何時間もチャートを見つめ、次の価格の動きを予測しようとすることですが、結局のところ、判断は非常にシンプルで、「上昇か下落か」、つまり「1か0か」という選択に尽きるのです。

このプロジェクトを進める中で、私は何時間もビットコインを対象に実験をおこないました。なぜビットコインなのか。それは暗号資産が「純粋なコード」で構成されているからです。四半期報告書や配当などのファンダメンタルは存在せず、そこには純粋に群衆心理が数値として符号化されています。

Pythonでの基本的な分析は、以下のように表現できます。

def analyze_words_frequency(self, prices):
    price_diff = np.diff(prices)
    bullish_mnemonics = []
    bearish_mnemonics = []
    
    for i in range(len(price_diff)):
        window = prices[max(0, i-4):i+1]
        if len(window) < 5:
            continue
        mnemonic = self.encode_price_movement(window)
        if price_diff[i] > 0:
            bullish_mnemonics.extend(mnemonic.split())
        else:
            bearish_mnemonics.extend(mnemonic.split())

さて、ここからが最も面白い部分です。実際の取引でこの手法がどのように機能するかを考えてみましょう。想像してみてください。チャートを見ているとき、通常のローソク足ではなく、「1011101」というバイナリシーケンスが目に入ったとします。一見すると意味不明に思えるかもしれません。しかし、実際にはそうではありません。このシーケンスは、十数個のテクニカル指標以上の情報を教えてくれることさえあるのです。

興味深いことに、このコードを同僚のトレーダーに見せたところ、最初はみな、なぜそんな複雑にするのかと首をかしげました。しかしその後、ある1人が興味深い発見をしました。特定のバイナリシーケンスは、確率論で考えられる以上に、強い値動きの前に出現する傾向があることが分かったのです。

もちろん、これは、私たちがお金を印刷するマシンを発明したという意味ではありません。 しかし、市場がバイナリコードを通じて私たちと会話しているという考えには、確かに魅力があります。あの映画『マトリックス』のように数字の背後に隠れたメッセージが存在すると思うと、ワクワクしませんか。

移動平均線と比較して

MetaTrader 5をPython経由で扱っているうちに、移動平均に対するまったく新しい視点を発見しました。もともとは単にターミナルをPythonに接続したいだけだったのですが、結果的にそれ以上に重要なものを見つけることになりました。

import MetaTrader5 as mt5
import numpy as np

def encode_ma_crossings(symbol, timeframe, ma_period=20):
    # Connect to the terminal
    if not mt5.initialize():
        print("MT5 initialization error")
        return None
        
    # Get data
    rates = mt5.copy_rates_from_pos(symbol, timeframe, 0, 1000)
    prices = np.array([rate[4] for rate in rates])  # Use Close prices
    
    # Calculate SMA and program crossover
    ma = np.convolve(prices, np.ones(ma_period)/ma_period, mode='valid')
    crossings = prices[ma_period-1:] > ma
    
    mt5.shutdown()
    return np.where(crossings, 1, 0)

最も興味深いことが起こったのは、ある奇妙なパターンに気づいたときでした。ビットコインにおいて、移動平均(MA)の近くで現れる「101」という並びが、強い値動きの前兆となることが多かったのです。特にボラティリティの高い局面では顕著でした。まるで市場が「跳ぶ前に助走している」かのように感じられました。

動きの強さ(モメンタム)について

「市場は眠っているか、走っているかのどちらかだ」という言葉を、私はよく仲間のトレーダーに話します。想像してみてください。この状態を単純な0と1に符号化できるとしたら。美しいと思いませんか。

def encode_momentum(symbol, timeframe, threshold=0.02):
    if not mt5.initialize():
        return None
        
    # Load data from MT5
    rates = mt5.copy_rates_from_pos(symbol, timeframe, 0, 1000)
    prices = np.array([rate[4] for rate in rates])
    
    # Calculate price changes
    price_changes = np.diff(prices) / prices[:-1]
    
    # Program strong movements
    strong_moves = np.abs(price_changes) > threshold
    momentum = np.zeros_like(price_changes)
    momentum[strong_moves] = np.sign(price_changes[strong_moves])
    
    mt5.shutdown()
    return momentum

このコードを使った最初の実験を、今でもよく覚えています。ETH/USDの1時間足チャートで実行してみたところ、なんと「000」という並び(低ボラティリティの期間)の後には、強い値動きが発生する確率が23%上昇することが分かったのです。もちろん、これが取引の黄金律というわけではありませんが、考えさせられる結果であることに間違いはありません。

ちなみに、分析に出来高を加えてみたところ、さらに興味深い結果が得られました。その詳細については次のセクションでお話ししますが、まずは皆さんご自身でもこれらのパターンを試してみてください。もしかすると、私が見落としていた発見があるかもしれません。

出来高について


さて、この出来高については少し面白い経緯があります。最初のうちは、ただの符号化パラメータの一つ程度にしか考えていませんでした。ところが、BTC/USDのパターンを分析し始めたところ、出来高がときに価格そのもの以上の情報を語っていることに気づいたのです。

import MetaTrader5 as mt5
import numpy as np
from datetime import datetime

def encode_volume_patterns(symbol, timeframe):
    if not mt5.initialize():
        return None
    
    # Take volume data
    rates = mt5.copy_rates_from_pos(symbol, timeframe, 0, 1000)
    volumes = np.array([rate[5] for rate in rates])
    
    # Calculate average volume for the last 20 bars
    avg_volume = np.convolve(volumes, np.ones(20)/20, mode='valid')
    
    # Code: 1 for volume above average and 0 - for below
    volume_code = volumes[19:] > avg_volume
    
    mt5.shutdown()
    return np.where(volume_code, 1, 0)

いちばん興味深かったのは何だと思いますか。出来高の「111」という並び、つまり、高出来高のバーが3本連続して出現するパターンが、しばしば大きな値動きの前に現れるのです。まるで「クジラたち(大口投資家)」がレース前にウォームアップしているかのようでした。特に4時間足チャートでは、その傾向が非常に明確に表れます。


時間足と符号化への影響

ある晩、私はコードの前に座り込み、同じパターンが時間足によって異なる結果を示す理由を考え続けていました。そして、ふと気づいたのです。もし同じ瞬間を複数の時間足から同時に観察したらどうだろうか、と。

def multi_timeframe_code(symbol, timeframes=[mt5.TIMEFRAME_M15, mt5.TIMEFRAME_H1, mt5.TIMEFRAME_H4]):
    if not mt5.initialize():
        return None
    
    combined_code = []
    
    for tf in timeframes:
        rates = mt5.copy_rates_from_pos(symbol, tf, 0, 100)
        prices = np.array([rate[4] for rate in rates])
        
        # Code direction on each timeframe
        price_direction = np.diff(prices) > 0
        combined_code.append(np.where(price_direction, 1, 0))
    
    mt5.shutdown()
    
    # Combine codes from different timeframes
    return np.array(combined_code)

その結果には本当に驚かされました。ある特定の組み合わせが、24時間のうち特定の時間帯にしか現れないことが分かったのです。たとえば、EUR/USDペアでは、15分足チャート上の「101」という並びが、アジア時間とヨーロッパ時間ではまったく異なる意味を持つのです。

こんな出来事もありました。ETHUSDTでこのシステムを使って取引していたときのことです。不思議なことに、毎回大きな値動きの直前には、1時間足チャートで「110」の並びが出現し、同時に4時間足チャートでは「101」が現れるのです。偶然かもしれません。その可能性はあります。ですが、同じことが5回も続いたときには、もはや単なる偶然とは思えなくなっていました。

ちなみに、短期足で「理想的」に見えるセットアップが、上位足の現実の前ではあっさり崩れてしまう様子を観察するのも面白いものです。今では、ポジションを取る前に少なくとも3つのタイムフレームでバイナリパターンを確認するようにしています。用心しすぎでしょうか。そうかもしれません。取引においてはやりすぎるくらいでちょうど良いのです。


ゼロエントロピー的な粗い・単純なバイナリパターンの探索と研究について

バイナリ化された市場パターンの中に法則性を探そうという発想が生まれた経緯をお話ししましょう。すべては、非常に単純な観察から始まりました。「どんな価格変動も、0と1の連なりとして表現できる」という気づきです。しかし、掘り下げていくほどに、その世界はどんどん興味深いものになっていきました。

import MetaTrader5 as mt5
import pandas as pd
import numpy as np
from datetime import datetime, timedelta
import base58
from sklearn.preprocessing import StandardScaler
from collections import Counter

def initialize_mt5():
    if not mt5.initialize():
        print("Initialize() failed")
        mt5.shutdown()
        return False
    return True

def get_eurusd_data(start_date, end_date, timeframe):
    rates = mt5.copy_rates_range("EURUSD", timeframe, start_date, end_date)
    df = pd.DataFrame(rates)
    df['time'] = pd.to_datetime(df['time'], unit='s')
    return df

class PriceDecoder:
    def __init__(self, df):
        self.df = df
        self.binary_patterns = []
    
    # Method 1: Encoding based on the movement direction
    def direction_encoding(self, window=10):
        binary = (self.df['close'] > self.df['close'].shift(1)).astype(int)
        pattern = ''.join(binary.astype(str).tail(window))
        return pattern, self.analyze_pattern(pattern)
    
    # Method 2: Encoding based on relation to MA
    def ma_encoding(self, ma_period=20, window=10):
        ma = self.df['close'].rolling(ma_period).mean()
        binary = (self.df['close'] > ma).astype(int)
        pattern = ''.join(binary.astype(str).tail(window))
        return pattern, self.analyze_pattern(pattern)
    
    # Method 3: Encoding according to movement strength
    def momentum_encoding(self, threshold=0.0001, window=10):
        returns = self.df['close'].pct_change()
        binary = (returns.abs() > threshold).astype(int)
        pattern = ''.join(binary.astype(str).tail(window))
        return pattern, self.analyze_pattern(pattern)
    
    # Method4: Volume encoding
    def volume_encoding(self, window=10):
        avg_volume = self.df['tick_volume'].rolling(window).mean()
        binary = (self.df['tick_volume'] > avg_volume).astype(int)
        pattern = ''.join(binary.astype(str).tail(window))
        return pattern, self.analyze_pattern(pattern)
    
    # Method 5: Fractal encoding
    def fractal_encoding(self, window=10):
        highs = self.df['high'].rolling(5, center=True).max()
        lows = self.df['low'].rolling(5, center=True).min()
        binary = ((self.df['high'] == highs) | (self.df['low'] == lows)).astype(int)
        pattern = ''.join(binary.astype(str).tail(window))
        return pattern, self.analyze_pattern(pattern)
    
    # Method 6: Volatility encoding
    def volatility_encoding(self, window=10):
        volatility = self.df['high'] - self.df['low']
        avg_volatility = volatility.rolling(20).mean()
        binary = (volatility > avg_volatility).astype(int)
        pattern = ''.join(binary.astype(str).tail(window))
        return pattern, self.analyze_pattern(pattern)
    
    # Method 7: Candlestick pattern encoding
    def candle_pattern_encoding(self, window=10):
        body = abs(self.df['close'] - self.df['open'])
        shadow = self.df['high'] - self.df['low']
        binary = (body > shadow/2).astype(int)
        pattern = ''.join(binary.astype(str).tail(window))
        return pattern, self.analyze_pattern(pattern)
    
    # Method 8: Entropy Encoding
    def entropy_encoding(self, window=10):
        returns = self.df['close'].pct_change()
        entropy = returns.rolling(window).apply(lambda x: np.sum(-x[x!=0]*np.log2(abs(x[x!=0]))))
        binary = (entropy > entropy.mean()).astype(int)
        pattern = ''.join(binary.astype(str).tail(window))
        return pattern, self.analyze_pattern(pattern)
    
    # Method 9: Convergence/divergence encoding
    def convergence_encoding(self, window=10):
        ma_fast = self.df['close'].rolling(5).mean()
        ma_slow = self.df['close'].rolling(20).mean()
        binary = (ma_fast > ma_slow).astype(int)
        pattern = ''.join(binary.astype(str).tail(window))
        return pattern, self.analyze_pattern(pattern)
    
    # Method 10: Price level encoding
    def price_level_encoding(self, window=10):
        pivot = (self.df['high'] + self.df['low'] + self.df['close']) / 3
        binary = (self.df['close'] > pivot).astype(int)
        pattern = ''.join(binary.astype(str).tail(window))
        return pattern, self.analyze_pattern(pattern)
    
    # Method 11: RSI momentum encoding
    def rsi_momentum_encoding(self, window=10, rsi_period=14):
        delta = self.df['close'].diff()
        gain = (delta.where(delta > 0, 0)).rolling(window=rsi_period).mean()
        loss = (-delta.where(delta < 0, 0)).rolling(window=rsi_period).mean()
        rs = gain / loss
        rsi = 100 - (100 / (1 + rs))
        binary = (rsi > 50).astype(int)
        pattern = ''.join(binary.astype(str).tail(window))
        return pattern, self.analyze_pattern(pattern)
    
    # Method 12: Cluster encoding
    def cluster_encoding(self, window=10):
        prices = self.df['close'].values.reshape(-1, 1)
        scaler = StandardScaler()
        prices_scaled = scaler.fit_transform(prices)
        binary = (prices_scaled > 0).astype(int).flatten()
        pattern = ''.join(map(str, binary[-window:]))
        return pattern, self.analyze_pattern(pattern)
    
    # Method 13: Extremum encoding
    def extremum_encoding(self, window=10):
        highs = self.df['high'].rolling(window).max()
        lows = self.df['low'].rolling(window).min()
        mid = (highs + lows) / 2
        binary = (self.df['close'] > mid).astype(int)
        pattern = ''.join(binary.astype(str).tail(window))
        return pattern, self.analyze_pattern(pattern)
    
    # Method 14: Trend encoding
    def trend_encoding(self, window=10):
        slope = pd.Series(np.nan, index=self.df.index)
        for i in range(window, len(self.df)):
            y = self.df['close'].iloc[i-window:i].values
            x = np.arange(window)
            slope[i] = np.polyfit(x, y, 1)[0]
        binary = (slope > 0).astype(int)
        pattern = ''.join(binary.astype(str).tail(window))
        return pattern, self.analyze_pattern(pattern)
    
    # Method 15: Hybrid encoding
    def hybrid_encoding(self, window=10):
        direction = (self.df['close'] > self.df['close'].shift(1)).astype(int)
        volume = (self.df['tick_volume'] > self.df['tick_volume'].rolling(window).mean()).astype(int)
        volatility = ((self.df['high'] - self.df['low']) > 
                     (self.df['high'] - self.df['low']).rolling(window).mean()).astype(int)
        binary = (direction & volume & volatility).astype(int)
        pattern = ''.join(binary.astype(str).tail(window))
        return pattern, self.analyze_pattern(pattern)
    
    def analyze_pattern(self, pattern, min_seq_len=2, max_seq_len=8):
        # Convert to base58 for compactness
        base58_pattern = base58.b58encode(pattern.encode()).decode()
        
        # Advanced repeatability analysis with different depths
        repeating_sequences = {}
        for seq_len in range(min_seq_len, max_seq_len + 1):
            sequences_at_depth = []
            for i in range(len(pattern) - seq_len + 1):
                sequence = pattern[i:i+seq_len]
                count = pattern.count(sequence)
                if count > 1:
                    sequences_at_depth.append({
                        'sequence': sequence,
                        'count': count,
                        'positions': [j for j in range(len(pattern)) if pattern[j:j+seq_len] == sequence]
                    })
            if sequences_at_depth:
                repeating_sequences[seq_len] = sorted(sequences_at_depth, 
                                                    key=lambda x: (x['count'], len(x['sequence'])), 
                                                    reverse=True)
        
        # Entropy analysis for different windows
        entropy_analysis = {}
        for window_size in range(2, min(9, len(pattern) + 1)):
            windows = [pattern[i:i+window_size] for i in range(len(pattern)-window_size+1)]
            window_entropy = {}
            for window in set(windows):
                count = windows.count(window)
                prob = count / len(windows)
                local_entropy = -prob * np.log2(prob) if prob > 0 else 0
                window_entropy[window] = {
                    'count': count,
                    'probability': prob,
                    'entropy': local_entropy
                }
            entropy_analysis[window_size] = window_entropy
        
        # Search for stable patterns (with zero entropy)
        stable_patterns = []
        for window_size, patterns in entropy_analysis.items():
            zero_entropy_patterns = {
                pattern: data for pattern, data in patterns.items() 
                if abs(data['entropy']) < 0.01 and data['count'] > 1
            }
            if zero_entropy_patterns:
                stable_patterns.append({
                    'window_size': window_size,
                    'patterns': zero_entropy_patterns
                })
        
        # Basic statistics
        ones_count = pattern.count('1')
        zeros_count = pattern.count('0')
        overall_entropy = 0
        if len(pattern) > 0:
            probabilities = [pattern.count(char)/len(pattern) for char in set(pattern)]
            overall_entropy = -sum(p * np.log2(p) for p in probabilities if p > 0)
        
        return {
            'base58': base58_pattern,
            'repeating_sequences': repeating_sequences,
            'stable_patterns': stable_patterns,
            'entropy_analysis': entropy_analysis,
            'ones_ratio': ones_count / len(pattern),
            'zeros_ratio': zeros_count / len(pattern),
            'overall_entropy': overall_entropy
        }

def main():
    if not initialize_mt5():
        return
    
    # Get data for the last month
    end_date = datetime.now()
    start_date = end_date - timedelta(days=30)
    df = get_eurusd_data(start_date, end_date, mt5.TIMEFRAME_H1)
    
    decoder = PriceDecoder(df)
    
    # Use all encoding methods
    methods = [
        ('Direction', decoder.direction_encoding),
        ('MA', decoder.ma_encoding),
        ('Momentum', decoder.momentum_encoding),
        ('Volume', decoder.volume_encoding),
        ('Fractal', decoder.fractal_encoding),
        ('Volatility', decoder.volatility_encoding),
        ('Candle Pattern', decoder.candle_pattern_encoding),
        ('Entropy', decoder.entropy_encoding),
        ('Convergence', decoder.convergence_encoding),
        ('Price Level', decoder.price_level_encoding),
        ('RSI Momentum', decoder.rsi_momentum_encoding),
        ('Cluster', decoder.cluster_encoding),
        ('Extremum', decoder.extremum_encoding),
        ('Trend', decoder.trend_encoding),
        ('Hybrid', decoder.hybrid_encoding)
    ]
    
    print("\nPrice Pattern Analysis Results:")
    print("-" * 50)
    
    for method_name, method in methods:
        pattern, analysis = method()
        print(f"\n{method_name} Encoding:")
        print(f"Pattern: {pattern}")
        print(f"Base58: {analysis['base58']}")
        print("\nStable Patterns with Zero Entropy:")
        for stable_group in analysis['stable_patterns']:
            print(f"\nWindow Size {stable_group['window_size']}:")
            for pattern, data in stable_group['patterns'].items():
                print(f"  {pattern}: appears {data['count']} times")
        
        print("\nRepeating Sequences by Length:")
        for length, sequences in analysis['repeating_sequences'].items():
            print(f"\nLength {length}:")
            for seq in sequences[:3]:  # show top 3 for each length
                print(f"  {seq['sequence']}: appears {seq['count']} times at positions {seq['positions']}")
        
        print(f"\nBasic Statistics:")
        print(f"Ones ratio: {analysis['ones_ratio']:.2f}")
        print(f"Zeros ratio: {analysis['zeros_ratio']:.2f}")
        print(f"Overall entropy: {analysis['overall_entropy']:.2f}")
    
    mt5.shutdown()

if __name__ == "__main__":
    main()

まるでトレーダーではなく、暗号解読者としてチャートを見ているような感覚を想像してみてください。ひとつひとつのバーが、市場という不思議なメッセージの「文字」に見えてくるのです。あとは、このメッセージがどんなアルファベットを使って書かれているのかを理解するだけです。

私はこれまでに、独自のロジックを持つ15種類の符号化手法を作成しました。

  • direction_encoding:最も単純な方法です。価格が前より上なら「1」、下なら「0」。まるで初心者向けのモールス信号のようです。
  • ma_encoding:より興味深い方法です。価格と移動平均線の交差を監視します。EUR/USDでテストしていた際、ヨーロッパ時間帯に特定の組み合わせが頻出するという興味深い傾向が見られました。
  • momentum_encoding:値動きの勢いを分析します。驚くべきことに、「000」(勢いの弱い期間)が続いた後には、急激なアクティビティ上昇が発生することが多いのです。
  • volume_encoding:出来高をバイナリ化して分析します。ビットコインで「10101」という並びを見つけたことがあり、これが強い値動きの前に高確率で出現していました。
  • fractal_encoding:フラクタルパターンをバイナリ形式で表現します。ビル・ウィリアムズのフラクタルを、0と1の世界で探すようなイメージです。
  • volatility_encoding:ボラティリティの符号化です。一部のパターンは驚くほど安定しており、そのエントロピーはほぼゼロに近づきます。
  • candle_pattern_encoding:ローソク足パターンをバイナリで表現します。このシステムでは、たとえば十字線は「101」として表されます。
  • entropy_encoding:価格変動の情報エントロピーを測定します。エントロピーがゼロ付近まで下がるとき、市場は何かを「仕掛けよう」としているのかもしれません。
  • convergence_encoding:移動平均線の収束・乖離をバイナリ化します。まさにテクニカル分析の定番を、新しい形で表現しています。
  • price_level_encoding:価格と主要レベルの関係を符号化します。特にキリの良い数字付近では、興味深いパターンが多く見られます。
  • rsi_momentum_encoding:RSIをバイナリ化した手法です。「101」という並びがほぼゼロエントロピーで現れるケースもありました。
  • cluster_encoding:クラスタ分析をバイナリで表現します。注文の集中ゾーンをコードとして探すような感覚です。
  • extremum_encoding:局所的な高値・安値を0と1の並びで表現します。重要な反転の前に特徴的なシーケンスが現れることがあります。
  • trend_encoding:トレンド方向をバイナリ化します。特に長期足では安定したパフォーマンスを示します。
  • hybrid_encoding:すべての手法を組み合わせたスーパーインジケーター的手法です。ただし、すべてがバイナリで表されます。

そして最も面白かったのが、analyze_patternメソッドを追加したときでした。この手法は単なる繰り返しパターンを探すのではなく、そのエントロピーを測定し、Base58に変換して圧縮し、ゼロエントロピーのパターンを抽出します。

驚いたのは、一部のパターンがあまりにも規則的に出現することです。たとえば、hybrid_encodingにおける「11001」という並びは、EUR/USDの強い値動きの前に頻繁に現れました。

カオス理論やバタフライ効果を覚えていますか。市場のこのカオスの中に、時折「秩序の島」のような、ほぼゼロエントロピーのパターンが現れるのです。まるで市場が一瞬だけ、自らの計画を私たちに語りかけてくるかのようでした。

次のセクションでは、これらのパターンを実際の取引にどう活用するかを見ていきます。その前に、ぜひコードを使って自分でも実験してみてください。もしかすると、あなた自身がまったく新しい市場の「バイナリコード」を発見するかもしれません。


価格における単純なバイナリパターンの分析

市場をバイナリ符号化して実験を重ねた結果、いくつかの予想外のパターンが見つかりました。ここでは、特に興味深い発見を紹介します。

まず注目すべきは、モメンタムパターン「1010111111」です。エントロピーは0.72、出現回数は43回。平均収益率は-0.01%、勝率は44.19%でした。数値としては地味ですが、最大利益は+0.34%、最大ドローダウンは-0.42%でした。適切なリスク管理を行えば、十分に可能性を感じさせるパターンです。

出来高面では、1と0の比率がおよそ7:3である「0111001111」という特徴的な並びが確認されました。 このパターンは1か月間で13回出現し、勝率は46.15%でした。平均収益率は-0.06%でしたが、最大利益は+0.28%に達しました。

そして、真の発見は収束(収束/乖離)パターンでした。ゼロエントロピーの「0000000000」という並びが、なんと1652回出現しました。同時に勝率は53.27%となり、最大利益は+0.68%に達しました。シグナル数を考えると、統計的に非常に意味のある結果です。

最も優れた統計を示した手法は以下の通りです。

  • フラクタルパターン「0110000100」:11回の取引で勝率63.64%、平均収益性+0.14%
  • ボラティリティパターン「0010001011」:2回の取引で勝率100%、平均収益性+0.21%
  • ローソク足パターン「1010111110」:3回の取引で勝率100%、平均収益性+0.04%

一方、hybrid_encoding(「0010000011」)は12回のシグナルで勝率25%、平均収益-0.04%でした。ただし、最大利益は+0.33%に達しており、適切なフィルタリングをおこなえば有効性が期待できます。

また、RSI Momentumパターン(「0010000001」)も興味深い結果を示しました。1か月間で3回しか出現しませんでしたが、すべてが損失取引でした。これは、逆方向へのエントリーシグナルとして有効である可能性を示唆しています。

これらの統計を踏まえると、最適な戦略は次のように整理できます。

  1. 基本シグナル:フラクタル符号化パターン(勝率63.64%)
  2. 確認シグナル:収束(高頻度・正の勝率)
  3. フィルター:ボラティリティおよびローソク足パターン(勝率100%)

今後のセクションでは、これらのパターンをどのように実際のトレード戦略へ落とし込むかを詳しく解説します。現時点で言えるのは、バイナリ符号化によって市場をまったく新しい視点で捉え、これまで見えなかった法則を発見できるということです。


ニューラルネットワークによる取引所のバイナリコード解読の試み

バイナリパターンを扱っているうちに、「この市場の独特な言語をコンピュータにも理解させることはできないか?」と考えるようになりました。こうして生まれたのが、CatBoostを使って将来の価格変動を予測するというアイデアです。

CatBoostの魅力は、カテゴリデータの処理に優れている点です。そして、私たちのバイナリパターンはまさにカテゴリデータ、つまり0と1の並びが、市場の状態をそれぞれ表しているのです。

システムの中心となるのがBinaryPatternPredictorクラスです。その主な役割は、バイナリパターンをニューラルネットワークが理解できる特徴量に変換することです。ここで用いたのがスライディングウィンドウ法です。一定期間(ルックバック期間)の履歴を取り出し、次のウィンドウで「1」が多いか「0」が多いかを予測します。

最も興味深かったのは、prepare_featuresメソッドに取り組んでいたときのことです。想像してみてください。それぞれのパターンが、単なる0と1の並びではなく、ひとつの特徴量セットに変わるのです。そこには、ウィンドウ内に含まれる「1」の数、最新の値動きのトレンド、他の符号化手法との相関関係といった追加の指標も含まれています。

モメンタム手法で最初の実験をおこなったときのことを今でも覚えています。コードは単純に見えます。

returns = self.decoder.df['close'].pct_change()
base_binary = (returns.abs() > 0.0001).astype(int)

しかし、この数行のコードの背後には、ひとつの哲学が隠れています。価格変動の力を、単純な0と1の並びに変換するという考え方です。

そして、特に興味深かったのがハイブリッド手法です。

direction = (self.decoder.df['close'] > self.decoder.df['close'].shift(1)).astype(int)
volume = (self.decoder.df['tick_volume'] > self.decoder.df['tick_volume'].rolling(self.lookback).mean()).astype(int)
volatility = ((self.decoder.df['high'] - self.decoder.df['low']) > 
              (self.decoder.df['high'] - self.decoder.df['low']).rolling(self.lookback).mean()).astype(int)
base_binary = (direction & volume & volatility)

ここでは、市場を値動きの方向性、出来高、ボラティリティの3つの異なる視点から統合しています。これらを組み合わせることで、まるでテクニカル分析の「三本の柱(スリーホエールズ)」を、バイナリ形式で再構築したようなものになります。

trainメソッドでは、あえてデータをシャッフルしませんでした(shuffle=False)。なぜかというと、実際の取引では「時系列的な一貫性」が極めて重要だからです。市場はランダムにパターンを散りばめているのではなく、ひとつひとつ積み重ねていくものだからです。

そして、predict_next_patternメソッドで予測をおこなう段階では、モデルの信頼度を評価する仕組みを追加しました。

prediction = self.model.predict_proba(last_window)[0]
return {
    'probability_more_ones': prediction[1],
    'probability_more_zeros': prediction[0],
    'prediction': 'More ones' if prediction[1] > 0.5 else 'More zeros',
    'confidence': max(prediction)
}

これは非常に有用であることが分かりました。モデルの信頼度が高いほど、予測が正確であることが多かったのです。

main()関数の中では、複数の符号化手法を同時に使って分析をおこないました。実際にやってみると、異なる手法同士の予測の食い違いが、すべてが一致した場合よりも多くの情報を与えてくれることが分かりました。まさに「三人寄れば文殊の知恵」いえ、「ひとつの頭も良いけれど、五つの符号化手法ならなお良し」といったところでしょうか。

特に印象的だったのは、モデルが一見して分かりにくいパターンを学習したことです。たとえば、「低ボラティリティ+出来高の上昇」という組み合わせが現れると、次のウィンドウで「1」が優勢になる傾向を正確に予測したのです。これは実際のところ、大きな値動きの前のポジション蓄積局面を示す典型的な状況でした。


ニューラルネットワークの結果

この実験の結果で本当に驚かされたのは、各符号化手法がまるで古典的なテクニカル指標のように、それぞれ独自の個性を示したことです。

まずはモメンタムから見てみましょう。これは本当に素晴らしい結果でした。正解率は95%、F1スコアは0.92でした。つまりモデルは、市場のモメンタムの動きをほぼ正確に読み取っているということです。特に興味深いのは、次のパターンで「1」が優勢となる確率を95%と予測した点です。これはすなわち、強い値動きの継続を示すシグナルそのものです。

出来高分析も期待を裏切りませんでした。正解率は83%で、適合率と再現率もともにおよそ0.83とバランスが取れており、大口投資家の動きを的確に捉えていることが分かります。特に「1」が優勢になる確率を94%の信頼度で予測した場面では、思わず古典的な格言「出来高はトレンドを確認する」を思い出しました。

一方で、収束手法は意外な結果を示しました。正確度は同じく83%でありながら、「0」が優勢になる確率を78%と予測したのです。これはモメンタムや出来高のシグナルと正反対の結果です。こうした乖離は、多くの場合、トレンド転換の接近を示すことがよくあります。

そして何より、ハイブリッド手法は真の発見と言えるものでした。正解率は91%、適合率は0.92で、複数の手法を組み合わせることで、より安定した結果を得られることが確認できました。興味深いことに、この手法でも「0」が優勢になると予測され(確率75%)、収束手法のシグナルを裏付ける結果となりました。

このモデルを実際のEUR/USDデータでテストしていたときのことを覚えています。ある日、すべての手法が高い精度を示していたにもかかわらず、予測内容は互いに食い違っていました。そして数時間後、鋭いトレンド転換が発生したのです。偶然でしょうか。そうかもしれません。ですが、それ以来、私はこのような手法間の「不一致」に特に注目するようになりました。

総じて、これらの結果は非常に興味深い示唆を与えてくれます。もしかすると市場は、バイナリパターンという形で私たちに語りかけているのかもしれません。ただし、私たちはまだその読み方を完全には理解していません。そして、少なくともこの実験の範囲では、ニューラルネットワークの方が人間よりもうまくそれを読み解いているようです。

ちなみに重要な点として、これらすべての指標は1時間足で得られたものです。異なる時間軸で同じ分析をおこなうと、まったく異なる結果になる可能性があります。特に3Dバーの概念を適用した場合には。それは、また別の研究テーマとして語るべきものになるでしょう。


結論

この記事の冒頭で、私たちは「市場の声をバイナリコードを通して聞くことはできるのか?」と問いかけました。数か月にわたる実験、何千行ものコード、そして無数の分析の末に、私はこう言えるようになりました。はい、市場は確かに独自の言語で私たちに語りかけています。そして今、私たちはその言葉を少しだけ理解できるようになったのです。

この研究で最も心を打たれたのは、価格変動を単純に0と1の並びに変換するだけで、テクニカル分析のまったく新しい視点が開けるということでした。もはや以前のようにチャートを見るのではなく、今ではあらゆる値動きの中に「巨大なデジタルメッセージの一部」を見ているのです。

ニューラルネットワークの結果も、特筆すべきものでした。モメンタムパターンで95%、出来高分析で83%、ハイブリッド手法で91%の精度。 これらは単なる数字ではありません。それは、市場の一見カオスに見える動きの中にも、秩序が潜んでいることを証明する結果なのです。そして、「どこを探せばよいのか」を知っている者だけが、それを見つけ出せるのです。

もちろん、これが取引の「賢者の石」だと考えるのはナイーブでしょう。市場はあまりにも複雑で、どんな高度なアルゴリズムをもってしても完全に説明することはできません。それでも、私たちは確かに市場を理解するための新しいツールを手に入れました。

私にとってこのプロジェクトは、単なるコード実験以上の意味を持つものとなりました。それは、「取引の世界には、常に革新の余地がある」ということを思い出させてくれたのです。 おそらく次の市場分析のブレイクスルーは、複雑な数学モデルからではなく、市場が自らのバイナリ言語で語りかけている声に耳を傾ける力から生まれるのかもしれません。

では、この先に何が待っているのでしょうか。私には、私たちはまだ旅の始まりに立っているように思えます。これからは、他の符号化手法の実験、タイムフレーム間の関係性の研究、そして古典的な分析手法との統合が待っています。しかし、最も大切なのは、市場の声に耳を傾け続けることです。市場は、聞く耳を持ち、心を革新に開いている者に対して、いつでもその秘密を分かち合おうとしているのです。そして、革新こそが進化の原動力なのです。 

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

添付されたファイル |
Price_Binary_v_1.py (27.43 KB)
最後のコメント | ディスカッションに移動 (2)
削除済み | 14 1月 2025 において 17:16
フォーラムのMOスレッドでは、フィーチャーの2値化と定量化は結局何もできなかったようだ :)
Intan Yahya
Intan Yahya | 11 9月 2025 において 05:31

あなたの記事を読む限り、理論的には有望だと思います。このインジケーター・プロジェクトが 具体化し、有効に活用できるようになるまで、私はあなたの次の更新をフォローしている。それまで、良い仕事を続けてください。

量子コンピューティングと取引:価格予測への新たなアプローチ 量子コンピューティングと取引:価格予測への新たなアプローチ
本記事では、量子コンピューティングを用いて金融市場における価格変動を予測するための革新的なアプローチについて説明します。主な焦点は、量子位相推定(QPE: Quantum Phase Estimation)アルゴリズムを適用して価格パターンのプロトタイプを見つけることであり、これによりトレーダーは市場データの分析を大幅に高速化できるようになります。
市場シミュレーション(第3回):パフォーマンスの問題 市場シミュレーション(第3回):パフォーマンスの問題
時には一歩下がってから前進する必要があります。本記事では、マウスインジケーターおよびChart Tradeインジケーターが正常に動作するようにするために必要なすべての変更についてご紹介します。さらにおまけとして、今後広く使用される他のヘッダーファイルにおける変更についても触れます。
学習中にニューロンを活性化する関数:高速収束の鍵は? 学習中にニューロンを活性化する関数:高速収束の鍵は?
本記事では、ニューラルネットワークの学習における異なる活性化関数と最適化アルゴリズムの相互作用に関する研究を紹介します。特に、古典的なADAMとその集団版であるADAMmを比較し、振動するACONやSnake関数を含む幅広い活性化関数での動作を検証します。最小構成のMLPアーキテクチャ(1-1-1)と単一の学習例を用いることで、活性化関数が最適化に与える影響を他の要因から切り離して観察します。本記事では、活性化関数の境界を利用したネットワーク重みの管理と重み反射機構を提案し、学習における飽和や停滞の問題を回避できることを示します。
取引におけるニューラルネットワーク:層状メモリを持つエージェント 取引におけるニューラルネットワーク:層状メモリを持つエージェント
層状メモリアプローチは、人間の認知プロセスを模倣することで、複雑な金融データの処理や新しいシグナルへの適応を可能にし、動的な市場における投資判断の有効性を向上させます。