在华尔街的交易员中流传着这样一句话:”历史不会简单重复,但会押着韵脚。”这句话用来形容谐波交易模式再合适不过。这种将斐波那契数列与几何图形结合的分析方法,就像在价格走势图中寻找隐藏的韵律。
谐波交易,一听这名字就挺玄乎的,仿佛在市场的波动里找到了某种隐藏的旋律。其实,说白了,谐波交易就是利用数学规律,尤其是斐波那契数列,去捕捉市场价格的周期性波动,并寻找潜在的交易机会。在这篇文章里,我们会从量化角度介绍谐波形态在金融市场的应用,并且以ABCD形态为例,深入解析一段自动交易策略代码,让你从理论到实战都能有所收获。
谐波交易的核心思想是:市场价格不是随机走的,而是有一定的比例和对称关系。很多交易者相信,市场的波动符合一些固定的数学规律,而这些规律可以帮助我们预测未来的走势。其合理性源于市场参与者的群体心理——当价格触及斐波那契关键比率时,交易者倾向于在此做出决策,形成支撑或阻力。
谐波模式分为ABCD、蝙蝠(Bat)、蝴蝶(Butterfly)等多种形态。本文将以ABCD形态为例,结合代码实战,展示如何用Python捕捉这类信号。
ABCD形态是最常见的谐波形态之一,它的基本结构如下:
当市场价格形成这个结构,并且符合这些比例关系时,我们就认为它形成了一个ABCD形态。当形态完成时,D点即为潜在的转折点。若D点低于C点(熊市ABCD),预示下跌;反之则为牛市ABCD,预示上涨。
使用肉眼判断谐波形态不仅需要丰富的市场经验,还要求交易者具备充沛的精力,长时间盯盘无疑是一项艰巨的任务。幸运的是,我们可以借助量化的力量,让计算机自动完成形态识别和交易决策,从而提高效率并减少人为误差。因此,我们选择在 优宽量化平台 上实现这一策略。
本段代码的核心目标是:自动识别ABCD形态,并基于形态信号执行交易决策。接下来,我们将逐步拆解代码的关键逻辑,解析其实现方式。
FIBB_RATIOS = [0.236, 0.382, 0.5, 0.618, 0.786, 1.0, 1.13, 1.272, 1.414, 1.618, 2.0, 2.24, 2.618]
这些比率来源于斐波那契数列,是谐波形态的关键。它们决定了市场价格回调和延伸的可能比例,在形态识别时会用到。
def is_eq(n, m, err=0.05, l_closed=False, r_closed=True):
"""判断数值是否在允许误差范围内相等"""
left = m if l_closed else m * (1 - err)
right = m if r_closed else m * (1 + err)
return (n >= left) and (n <= right)
这段代码的作用是 判断一个数值n是否接近某个目标值m(考虑误差范围)。比如,我们检测AB段是否接近0.618,就可以用这个函数来判断。
def is_in(n, l, r, err=0.05, l_closed=True, r_closed=True, strict=True):
"""判断数值是否在区间内"""
left = l if l_closed else l * (1 - err)
right = r if r_closed else r * (1 + err)
if strict:
return any(is_eq(n, f_rate, err) for f_rate in FIBB_RATIOS if left <= f_rate <= right)
return (n >= left) and (n <= right)
这个函数用来判断一个数是否落在指定区间 [l, r] 之内,比如判断 CD段的延伸是否在1.13到2.618之间。
def get_zigzag(df, period=14):
ZigZag指标用于筛选市场的 重要转折点,减少市场噪音。它的工作原理是:如果价格在某个周期(period)内达到新高或新低,就认为形成了一个关键点。
highest_high = ta.MAX(df['High'].values[:i], timeperiod=period)[-1]
lowest_low = ta.MIN(df['Low'].values[:i], timeperiod=period)[-1]
这里用 ta.MAX
和 ta.MIN
找到 最近period根K线的最高点和最低点,用来判断市场是否发生了新高或新低。
if new_high and not new_low:
if direction != 1:
direction = 1
changed = True
elif new_low and not new_high:
if direction != -1:
direction = -1
changed = True
如果价格创出新高,方向变成向上;如果创出新低,方向变成向下。
最终,函数返回一个 ZigZag 点列表,每个点包含时间、价格和方向。
def detect_abcd(points, err=0.05):
ABCD形态检测的逻辑:
计算AB、BC、CD段的比例:
AB = abs(B[1]-A[1]) / abs(X[1]-A[1])
BC = abs(C[1]-B[1]) / abs(A[1]-B[1])
CD = abs(D[1]-C[1]) / abs(B[1]-C[1])
这里分别计算了AB段、BC段、CD段的比例关系。
判断形态是否符合ABCD模式:
is_AB_valid = is_in(AB, 0.382, 0.886, err=err)
is_CD_valid = is_in(CD, 1.13, 2.618, err=err)
is_ABCD_eq = is_eq(BC/CD, 1, err=err)
如果这些条件都满足,就认为找到了一个有效的ABCD形态。
当发现ABCD形态后,代码会决定是否下单:
if latest_pattern['direction'] == 'Bullish':
exchange.CreateOrder(symbol, 'buy', -1, 1)
if latest_pattern['direction'] == 'Bearish':
exchange.CreateOrder(symbol, 'sell', -1, 1)
此外,还有一个移动止盈的逻辑:
if curPos.Type % 2 == 0 and (r[-1].Close < bestPrice * (1 - 0.01)):
如果当前价格比最佳价格低 1%,就止盈。
为了验证ABCD形态交易策略的有效性,我们将代码应用于 纯碱期货(SA888) 的 5分钟K线数据(2025年2月-3月),并观察其在实际市场中的表现。
示例回测图表(ABCD形态标记):
该测试结果表明,ABCD形态交易策略在趋势市场中表现良好,但在震荡行情下可能会出现一定程度的误判。因此,结合 其他技术分析工具(如成交量、均线) 进行信号过滤,可能会进一步提升策略的胜率和稳定性。
谐波交易策略的核心在于 数学比率的精确性,它的最大优势是避免情绪化交易,但同时也带来了一些局限性。
总结:
谐波交易本质上是一种 捕捉市场共振的技术分析方法,在某些市场环境下具有优势,但需要结合 动态参数优化 和 信号过滤 来提高胜率。
为了提高ABCD形态策略的适应性和盈利能力,我们可以从以下几个方向进行优化:
动态误差调整:
多形态叠加,扩大交易机会:
量价确认,提高信号可靠性:
机器学习辅助形态识别:
为了更深入理解谐波交易策略并应用到实盘交易中,以下资料和建议值得参考:
谐波交易并非“圣杯”,但它能帮助交易者更理性地分析市场结构,并寻找高概率的交易机会。结合量化手段后,我们可以让这一策略变得更加智能化、更适应市场变化。
'''backtest
start: 2025-02-02 00:00:00
end: 2025-03-04 00:00:00
period: 5m
basePeriod: 1m
exchanges: [{"eid":"Futures_CTP","currency":"FUTURES"}]
'''
MAIN_FIBB_RATIOS = [0.618, 1.618]
SECOND_FIBB_RATIOS = [0.786, 0.886, 1.13, 1.27]
ALT_FIBB_RATIOS = [0.382, 0.5, 0.707, 1.41, 2.0, 2.24, 2.618, 3.14, 3.618]
AB_CD = [1.27, 1.618, 2.236]
FIBB_RATIOS = [*MAIN_FIBB_RATIOS, *SECOND_FIBB_RATIOS, *ALT_FIBB_RATIOS]
import pandas as pd
import talib as ta
# 斐波那契常用比率
FIBB_RATIOS = [0.236, 0.382, 0.5, 0.618, 0.786, 1.0, 1.13, 1.272, 1.414, 1.618, 2.0, 2.24, 2.618]
def is_eq(n, m, err=0.05, l_closed=False, r_closed=True):
"""判断数值是否在允许误差范围内相等"""
left = m if l_closed else m * (1 - err)
right = m if r_closed else m * (1 + err)
return (n >= left) and (n <= right)
def is_in(n, l, r, err=0.05, l_closed=True, r_closed=True, strict=True):
"""判断数值是否在区间内,并考虑严格模式"""
left = l if l_closed else l * (1 - err)
right = r if r_closed else r * (1 + err)
if strict:
return any(is_eq(n, f_rate, err) for f_rate in FIBB_RATIOS if left <= f_rate <= right)
return (n >= left) and (n <= right)
def get_zigzag(df, period=14):
if len(df) < period:
return
"""获取ZigZag指标点"""
zigzag = []
direction = 0
for i in range(1, len(df)):
# 使用talib计算最高价和最低价
highest_high = ta.MAX(df['High'].values[:i], timeperiod=period)[-1]
lowest_low = ta.MIN(df['Low'].values[:i], timeperiod=period)[-1]
new_high = df['High'].iloc[i] >= highest_high
new_low = df['Low'].iloc[i] <= lowest_low
changed = False
if new_high and not new_low:
if direction != 1:
direction = 1
changed = True
elif new_low and not new_high:
if direction != -1:
direction = -1
changed = True
if new_high or new_low:
if changed or not zigzag:
price = df['High'].iloc[i] if direction == 1 else df['Low'].iloc[i]
zigzag.append((df['Time'].iloc[i], price, direction))
else:
last_idx, last_price, last_dir = zigzag[-1]
if direction == 1 and df['High'].iloc[i] > last_price:
zigzag[-1] = (df['Time'].iloc[i], df['High'].iloc[i], direction)
elif direction == -1 and df['Low'].iloc[i] < last_price:
zigzag[-1] = (df['Time'].iloc[i], df['Low'].iloc[i], direction)
return zigzag
def detect_abcd(points, err=0.05):
"""检测ABCD形态"""
if len(points) < 5:
return None
# 提取最近5个点构成XABCD形态
X, A, B, C, D = points[-5:]
# 计算各段比率
AB = abs(B[1]-A[1]) / abs(X[1]-A[1])
BC = abs(C[1]-B[1]) / abs(A[1]-B[1])
CD = abs(D[1]-C[1]) / abs(B[1]-C[1])
# 形态规则判断
is_AB_valid = is_in(AB, 0.382, 0.886, err=err)
is_CD_valid = is_in(CD, 1.13, 2.618, err=err)
is_ABCD_eq = is_eq(BC/CD, 1, err=err)
if is_AB_valid and is_CD_valid and is_ABCD_eq:
direction = 'Bullish' if D[1] < C[1] else 'Bearish'
return {
'type': 'ABCD',
'points': (X, A, B, C, D),
'ratios': {'AB': AB, 'BC': BC, 'CD': CD},
'direction': direction
}
return None
# 使用示例
def main():
df = pd.DataFrame(columns=['Time', 'Open', 'High', 'Low', 'Close', 'Volume', 'OpenInterest'])
last_pattern_time = None # 用于记录上次绘制形态的时间
bestPrice = None
symbol = 'SA888'
while True:
# 获取最新数据
r = exchange.GetRecords(symbol, PERIOD_M5)
pos = exchange.GetPositions(symbol)
if r is None or len(r) < 2 or pos is None:
continue
new_r = r[-2] # 取最新的 K 线数据
# 检查 Time 是否已存在
if new_r['Time'] not in df['Time'].values:
new_row = pd.DataFrame([new_r]) # 转换为 DataFrame
df = pd.concat([df, new_row], ignore_index=True) # 追加新数据
# 转换数据类型
df[['High', 'Low', 'Close']] = df[['High', 'Low', 'Close']].astype(float)
# 获取ZigZag点
zigzag_points = get_zigzag(df, period=14)
if zigzag_points is None or len(zigzag_points) < 5:
continue
# 检测最新形态
latest_pattern = None
for i in range(4, len(zigzag_points)):
segment = zigzag_points[i-4:i+1]
pattern = detect_abcd(segment, err=0.2)
if pattern:
pattern_time = pattern['points'][-1][0] # 使用D点时间作为形态时间
# 仅保留时间最新的形态
if (latest_pattern is None) or (pattern_time > latest_pattern['points'][-1][0]):
latest_pattern = pattern
# 仅当检测到新形态时绘图
if latest_pattern and (last_pattern_time != latest_pattern['points'][-1][0]):
Log('检测到ABCD形态', latest_pattern)
last_pattern_time = latest_pattern['points'][-1][0]
if len(pos) == 0:
if latest_pattern['direction'] == 'Bullish':
exchange.CreateOrder(symbol, 'buy', -1, 1)
bestPrice = r[-1].Close
if latest_pattern['direction'] == 'Bearish':
exchange.CreateOrder(symbol, 'sell', -1, 1)
bestPrice = r[-1].Close
if len(pos) == 1: # 检查是否有持仓
curPos = pos[0] # 获取当前持仓信息
# 更新最佳价格
bestPrice = (
max(bestPrice, r[-1].Close) if curPos.Type % 2 == 0 else min(bestPrice, r[-1].Close)
)
# 多头移动止盈逻辑
if curPos.Type % 2 == 0 and (r[-1].Close < bestPrice * (1 - 0.01)):
Log(f'多头移动止盈:最佳点位:{bestPrice},当前点位:{r[-1].Close}#00ff00')
exchange.CreateOrder(symbol, 'closebuy', -1, 1)
# 空头移动止盈逻辑
if curPos.Type % 2 == 1 and (r[-1].Close > bestPrice * (1 + 0.01)):
Log(f'空头移动止盈:最佳点位:{bestPrice},当前点位:{r[-1].Close}#00ff00')
exchange.CreateOrder(symbol, 'closesell', -1, 1)
Sleep(1000 * 60)