分析交易所价格的二进制代码(第一部分):技术分析的新视角
引言
你还记得《黑客帝国》中尼奥将世界视为绿色二进制代码的场景吗?但如果我们用这种方式看待交易所图表呢?在日常交易中,我常常思考:如果我们把市场的波动想象成某种代码或语言,是否有可能“听懂”市场试图传递给我们的信息?
这个想法是在分析比特币图表时意外产生的。观察价格的暴涨与暴跌时,我注意到某些波动组合会重复出现,就像单词中的字母一样。这让我思考:如果我们把这些波动编码为二进制格式,并尝试“解读”生成的信息,会怎样?
听起来很疯狂吗?也许是吧。但请记住:技术分析不正是基于寻找形态吗?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次,全部为亏损,这可能预示着反向开仓的强烈信号。
根据获得的统计数据,最优策略可能如下:
- 基础信号:分形编码模式(63.64%的成功交易)
- 确认信号:收敛模式(高频出现且胜率为正)
- 过滤信号:波动率和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
注意: MetaQuotes Ltd.将保留所有关于这些材料的权利。全部或部分复制或者转载这些材料将被禁止。
本文由网站的一位用户撰写,反映了他们的个人观点。MetaQuotes Ltd 不对所提供信息的准确性负责,也不对因使用所述解决方案、策略或建议而产生的任何后果负责。
交易中的神经网络:搭配区段注意力的参数效率变换器(终篇)
《精通日志记录(第二部分):格式化日志》
开发回放系统(第 74 部分):新 Chart Trade(一)
从基础到中级:联合(一)
从您的文章来看,理论上似乎很有前途。我将继续关注你的下一次更新,直到这个指标项目 得以实现并派上用场。在此之前,请继续努力。