English Русский Español Deutsch 日本語 Português
preview
分析交易所价格的二进制代码(第一部分):技术分析的新视角

分析交易所价格的二进制代码(第一部分):技术分析的新视角

MetaTrader 5指标 |
488 2
Yevgeniy Koshtenko
Yevgeniy Koshtenko

引言

你还记得《黑客帝国》中尼奥将世界视为绿色二进制代码的场景吗?但如果我们用这种方式看待交易所图表呢?在日常交易中,我常常思考:如果我们把市场的波动想象成某种代码或语言,是否有可能“听懂”市场试图传递给我们的信息?

这个想法是在分析比特币图表时意外产生的。观察价格的暴涨与暴跌时,我注意到某些波动组合会重复出现,就像单词中的字母一样。这让我思考:如果我们把这些波动编码为二进制格式,并尝试“解读”生成的信息,会怎样?

听起来很疯狂吗?也许是吧。但请记住:技术分析不正是基于寻找形态吗?K线图不也是一种价格波动的编码系统吗?毕竟,市场本身就是一个巨大的、以数字方式编码的信息流。

在本文中,我想分享我的实验:将价格波动转化为有意义的二进制代码序列。我们将探讨如何将市场行为的各个方面——从简单的价格波动到复杂形态——转换为二进制格式,并尝试在这些代码中找到能帮助我们更好理解市场语言的规律。

是的,这是一种非传统的方法。但交易的本质不正是如此——寻找新的视角,看见他人忽略的东西吗?

让我们一起深入探索,看看这条路会通向何方……


将价格波动表示为二进制代码的概念

我的方法基于一个简单但有趣的想法:任何价格波动都可以表示为一系列的 0 和 1。想一想:当我们看图表时,我们看到的是什么?上涨与下跌、强波动与弱波动、高于或低于均线的成交量。实际上,我们已经在不知不觉中把这些信息在脑中转换成了二进制格式!

下面是一个简单的示例。假设我们看到连续三根 K 线:一根上涨、一根下跌、再一根上涨。在最原始的表示中,这可以编码为“101”。但如果我们加入更多参数呢?例如,是否应考虑波动的强度、交易量,或相对于移动平均线的位置?

在我的实验中,我发现即使是基于“涨跌”原则的基础编码也能产生有趣的结果。然而,真正的魔力开始于我们开始考虑多个时间框架时。想象一下:小时图上的“101”序列在日线或周线图上可能看起来完全不同。这就形成了一种数据矩阵,其中每个价格波动都由一个独特的二进制序列描述。

在分析加密货币时,会得到特别有趣的结果。比特币的波动性在二进制表示中形成了清晰、明确的形态。有时这些序列类似于摩尔斯电码——同样的点和划的节奏舞蹈,只是这里变成了价格的涨跌。

当然,怀疑者可能会说这只是经典技术分析的复杂版本。但进步的本质不正是如此——从新的角度看待熟悉的事物吗?毕竟,K线图也曾被视为异类,但如今没有交易员能离开它。

在接下来的章节中,我们将详细探讨编码价格波动的各种方法,以及这些信息如何在真实交易中帮助我们。与此同时,我建议你亲自看看自己最喜欢的图表,尝试在其中看到 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())

而现在最有趣的是它在真实交易中是如何运作的。想象一下:你看着图表,看到的不是普通的K线,而是一串“1011101”的序列。这看起来像是胡言乱语吗?但它并不是!这个序列能告诉你的,可能比十几个技术指标还要多。

有趣的是,当我把这个代码展示给交易伙伴们看时,他们一开始都指着我的太阳穴转了转手指。“为什么要搞得这么复杂?”他们说。然后其中一个人注意到了一个有趣的现象:某些二进制序列在强波动前出现的频率,比概率论所预期的要高得多。

当然,这并不意味着我们发明了一台印钞机。但你必须承认,市场通过二进制代码与我们沟通的这个想法,本身就很有吸引力。就像那部《黑客帝国》电影里一样,还记得吗?

相对于移动平均线

通过 Python 使用 MetaTrader 5 时,我发现了一个看待移动平均线的全新视角。你知道,结果很有趣——我本只是想将终端连接到 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)

最有趣的事情始于我注意到一个奇怪的模式:在比特币上,移动平均线附近的“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 的小时图上运行它,你猜怎么着?结果发现,在“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”序列(连续三根高量K线)经常在重大行情前出现。就像“鲸鱼”在比赛前热身一样。在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,我注意到一个奇怪的现象——每次重大波动前,小时图上都会出现“110”序列,而同时在4小时图上——会出现“101”。巧合?有可能。但在第五次之后,我不再那么确定了。

顺便说一句,看着一些低时间框架上的“理想”结构在高时间框架的残酷现实面前崩溃,是件很有趣的事。现在,在入市前,我总是至少检查三个时间框架上的二进制模式。双重确认?也许是吧。但在交易中,宁可做过,不可错过。


寻找并研究零熵的二进制粗糙与朴素模式。

让我告诉你,在二进制市场模式中寻找模式的想法是如何诞生的。一切都始于一个简单的观察——任何价格波动都可以表示为一串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()

想象一下,你不再以交易者的身份看待图表,而是以一个解密者的身份。每一根K线都是市场这条奇怪信息中的一个字母。你只需弄明白它用的是哪一套字母表。

我创建了15种不同的编码方法,每一种都有其自身的逻辑:

  • direction_encoding —— 最简单的方法。价格比前一根高吗?是,1。低吗?是,0。就像初学者的摩尔斯电码。
  • ma_encoding —— 更有趣一些。监测价格与移动平均线的交叉。我在测试EURUSD时注意到一个有趣的现象——某些组合在欧洲交易时段出现得更频繁。
  • momentum_encoding —— 这里我们关注的是运动的强度。你知道最令人惊讶的是什么吗?三个连续的0(一段弱势动量期)之后,往往紧跟着一次剧烈的活动激增。
  • volume_encoding —— 二进制格式的成交量分析。我记得有一次在比特币上注意到一个奇怪的模式——“10101”,它在成交量上几乎总是预示着一次强烈的波动。
  • fractal_encoding —— 二进制形式的分形模式。就像在寻找比尔·威廉姆斯的分形,只不过是在0和1的世界里。
  • volatility_encoding —— 对波动性进行编码。有趣的是,某些波动性模式表现得如此稳定,以至于它们的熵趋近于零。
  • candle_pattern_encoding —— 经典K线形态的二进制表示。在这个系统中,十字星(Doji)看起来像“101”。
  • entropy_encoding —— 这里我们测量价格波动的信息熵。当熵接近零时——准备好迎接意外吧。
  • convergence_encoding —— 二进制格式的移动平均线收敛与发散。技术分析的经典,以新的格式呈现。
  • price_level_encoding —— 对价格与关键水平位的关系进行编码。在整数位附近会出现特别有趣的模式。
  • rsi_momentum_encoding —— 二进制形式的RSI。结果发现,RSI上的某些“101”组合几乎具有零熵。
  • cluster_encoding —— 二进制表示的价格集群分析。就像在寻找订单累积区,只不过是在代码中。
  • extremum_encoding —— 以0和1的形式表示局部最高点和最低点。有时在重要反转前会出现非常特征性的序列。
  • trend_encoding —— 二进制形式的趋势方向。该方法在较高时间框架上表现良好。
  • hybrid_encoding —— 所有方法的组合。就像一个超级指标,只是以二进制格式呈现。

最有趣的事情始于我添加了 analyze_pattern 方法。这个方法不仅仅是寻找重复序列——它测量它们的熵,将其转换为base58以便压缩,并识别出零熵的模式。

你知道最让我震惊的是什么吗?某些模式的出现如此规律,以至于不可能是偶然。例如,hybrid_encoding中的“11001”序列常常在EURUSD的强烈波动前出现。

你还记得混沌理论和蝴蝶效应吗?那么,有时在这价格波动的混沌中,会出现秩序的孤岛——几乎零熵的模式。就好像市场在那一瞬间透露了它的计划……

在接下来的章节中,我们将讨论如何在真实交易中使用这些模式。与此同时,我建议我们先用代码做实验。也许你会在市场的二进制代码中找到自己独特的模式?


分析价格中的单纯二进制模式

在分析市场二进制编码的实验结果时,我发现了几个意想不到的模式。让我们看看其中最有趣的发现。

首先,请注意动量模式“1010111111”。令人惊讶的是,尽管熵为0.72,这个模式在我们的时间框架内出现了43次!平均盈利为-0.01%,胜率为44.19%。乍看之下结果并不起眼,但最大盈利达到了+0.34%,最大回撤为-0.42%。这表明该模式在适当的风险管理下具有潜力。

在成交量方面,我们看到特征序列“0111001111”,其1与0的比例为70/30。该模式在一个月内出现了13次,胜率为46.15%。有趣的是,尽管平均回报为负(-0.06%),最大盈利却达到了+0.28%。

真正的发现是收敛(收敛/发散)模式。序列“0000000000”具有零熵,出现了惊人的1652次!同时,胜率为53.27%,最大盈利达到+0.68%。考虑到信号数量,这是一个具有统计显著性的结果。

在所有方法中,表现最佳统计的是:

  • 分形模式“0110000100”:11笔交易中胜率63.64%,平均盈利+0.14%
  • 波动率模式“0010001011”:2笔交易中胜率100%,平均盈利+0.21%
  • K线模式“1010111110”:3笔交易中胜率100%,平均盈利+0.04%

至于混合编码(“0010000011”),它显示了12个信号,胜率25%,平均回报-0.04%。然而,最大盈利达到+0.33%,这表明该方法在适当的信号过滤下具有潜力。

RSI动量模式(“0010000001”)显示出特别有趣的结果。一个月内仅出现3次,全部为亏损,这可能预示着反向开仓的强烈信号。

根据获得的统计数据,最优策略可能如下:

  1. 基础信号:分形编码模式(63.64%的成功交易)
  2. 确认信号:收敛模式(高频出现且胜率为正)
  3. 过滤信号:波动率和K线模式(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)

在这里,我们结合了市场的三种不同视角:运动方向、成交量和波动性。我们得到了类似技术分析中的“三巨头”的东西,只不过是以二进制形式呈现的。

在 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%的准确率和0.92的F1分数表明,模型几乎能精确读取市场的动量运动。特别有趣的是,预测显示下一个模式中1占优的概率高达95%。实际上,这就是强趋势将持续的信号。

成交量分析也毫不逊色。83%的准确率和平衡的指标(精确率与召回率也都在0.83左右),使其完美捕捉大玩家的活动。当我看到预测以94%的置信度认为1占优时,我立刻想起了经典规则——“成交量确认趋势”。

但收敛方法带来了一个惊喜。同样是83%的准确率,它却以78%的置信度预测0将占优。这与动量和成交量信号直接矛盾!这种分歧往往预示着趋势反转即将来临。

混合方法则成为了一个真正的发现。91%的准确率和0.92的精确率表明,结合不同方法能产生更稳健的结果。有趣的是,它也预测0占优(75%概率),印证了收敛方法的信号。

我记得在真实EURUSD数据上测试这个模型时。有一天,所有方法都表现出高准确率,但它们的预测却不一致——就像现在一样。几小时后,市场发生了剧烈的趋势反转。巧合?也许是吧。但从那时起,我开始密切关注方法之间的这种“分歧”。

总体来看,这些结果引出了一个有趣的思考:也许市场真的是通过二进制模式在和我们对话。只是我们并不总是知道如何正确解读它们。而神经网络似乎比人类更擅长这件事。

顺便提一句,重要的一点是,所有这些指标都是在1小时时间框架上获得的。在其他时间间隔上,情况可能完全不同。尤其是在应用3D K线概念时……但这是另一个研究课题了……


结论

在本文开头,我们问自己:是否有可能通过二进制代码听到市场的声音?经过数月的实验、成千上万行代码和无数小时的分析,我可以说——是的,市场确实在用它独特的语言与我们对话。而现在,我们已经学会了一点如何理解它。

你知道这项研究中最让我震撼的是什么吗?将价格运动转化为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 线程中,特征的二值化和定量化似乎没有任何结果:)
Intan Yahya
Intan Yahya | 11 9月 2025 在 05:31

从您的文章来看,理论上似乎很有前途。我将继续关注你的下一次更新,直到这个指标项目 得以实现并派上用场。在此之前,请继续努力。

交易中的神经网络:搭配区段注意力的参数效率变换器(终篇) 交易中的神经网络:搭配区段注意力的参数效率变换器(终篇)
在之前的工作中,我们讨论了 PSformer 框架的理论层面,其中包括经典变换器架构的两大创新:参数共享(PS)机制,以及时空区段注意力(SegAtt)。在本文中,我们继续实现所提议方式的 MQL5 版本。
《精通日志记录(第二部分):格式化日志》 《精通日志记录(第二部分):格式化日志》
在本文中,我们将探讨如何在类库中创建和应用日志格式化工具。我们将从格式化工具的基本结构讲起,一直到样例的实现。到本文结束时,您将掌握在该库中格式化日志的必要知识,并理解其背后的工作原理。
开发回放系统(第 74 部分):新 Chart Trade(一) 开发回放系统(第 74 部分):新 Chart Trade(一)
在本文中,我们将修改本系列关于 Chart Trade 中显示的最后一段代码。这些变化对于使代码适应当前的回放/模拟系统模型是必要的。此处提供的内容仅用于教育目的。在任何情况下,除了学习和掌握所提出的概念外,都不应出于任何目的使用此应用程序。
从基础到中级:联合(一) 从基础到中级:联合(一)
在这篇文章中,我们将探讨什么是联合。在这里,通过实验,我们将分析可以使用联合的第一种构造。然而,这里展示的只是后续文章将涵盖的一组概念和信息的核心部分。此处提供的内容仅用于教育目的。在任何情况下,除了学习和掌握所提出的概念外,都不应出于任何目的使用此应用程序。