[TOC]

Este artículo esUna ciencia popular sobre la transformada wavelet para el trading prácticoEl código es una versión de enseñanza simplificada (que omite pasos complejos como la descomposición de múltiples niveles, la eliminación de ruido del umbral y la reconstrucción de la transformada inversa de wavelets estándar) y que conserva solo las ideas centrales:Utilice coeficientes wavelet para realizar un suavizado multiescala de los precios para extraer información sobre tendencias.Adecuado para el desarrollo de estrategias y validación rápida, pero no para investigación académica o publicación de artículos.
Quienes navegan frecuentemente sobre temas financieros y cuantitativos en Zhihu seguramente habrán visto este escenario:
Algunos “expertos” siguen diciendo:
Dejó a todos atónitos, como si dominara el arma nuclear del comercio cuantitativo.
¿Pero quieres que te muestre el código?
“Esto… es un secreto comercial y no puedo revelarlo”.
Pídale que explique el principio.
“Esto… implica matemáticas avanzadas, no lo entenderías ni aunque te lo explicara.”
Hoy, exploraremos los temas mencionados frecuentemente por estos “expertos de Zhihu”, presentaremos las aplicaciones prácticas de la transformada wavelet en el mercado financiero y ayudaremos a todos a desarrollar una comprensión correcta de esta tecnología.
Imagina que estás escuchando una canción, pero hay ruido de fondo en la grabación:
Grabación original = voz humana + ruido de fondo + estática
La transformada wavelet es como unaFiltros inteligentes:
Pasar al mercado financiero:
Precio original = Tendencia real + Fluctuaciones a corto plazo + Ruido aleatorio
La transformada wavelet puede ayudarle a:
La esencia de la transformada wavelet esDescomponer la señal original utilizando un conjunto específico de “funciones base” (wavelets).。
Imagina que quieres describir la apariencia de una persona:
En precios financieros:
Serie de precios original = Función base 1 × Peso 1 + Función base 2 × Peso 2 + … + Ruido
Las funciones base son las “plantillas” correspondientes a los coeficientes wavelet.Los diferentes tipos de wavelets (Haar, Daubechies, Mexican Hat, etc.) utilizan diferentes plantillas, al igual que se utilizan diferentes “extractores de características” para descomponer los precios.
La transformada wavelet es esencialmente una…Banco de filtros multiescala:
Filtro de alta frecuencia → Captura fluctuaciones rápidas (ruido diario, fluctuaciones de nivel de tick) Filtro de frecuencia intermedia → Captura tendencias de mediano plazo (bandas que van desde unas pocas horas a unos pocos días). Filtros de baja frecuencia → Captura tendencias a largo plazo (tendencias semanales y mensuales).
¿Por qué se llama “wavelet”?
El problema de usar ondas sinusoidales para analizar precios financieros: Las ondas sinusoidales presuponen que la señal se repite periódicamente, ¡pero los mercados financieros no! BTC podría subir un 10 % hoy y bajar un 8 % mañana, sin ningún comportamiento cíclico.
Ventajas de los wavelets:Análisis de localizaciónPuede decirle “la tendencia de los precios fue principalmente al alza entre las 3 p. m. y las 5 p. m. del 20 de diciembre de 2025”, en lugar de una conclusión general como “el mercado en general estaba fluctuando”.
La transformada wavelet esReversible¡Esto es muy importante!
Precio original —> Descomposición wavelet —> Componente de tendencia + Componente de volatilidad + Componente de ruido Componente de tendencia + componente de volatilidad + componente de ruido —> reconstrucción wavelet —> precio original
Proceso de refactorizaciónConsiste en descomponer los distintos componentes.Combine selectivamente la espalda:
Utilice únicamente componentes de tendencia durante la reconstrucción
Después de la descomposición, se obtuvo lo siguiente
En las transacciones reales, normalmenteReconstruir sólo la parte de baja frecuencia(Tendencia) Los componentes de alta frecuencia (ruido) se descartan directamente. Este es el principio de la “reducción de ruido” de wavelets.
Saltemos las fórmulas integrales complejas y expliquémoslo en términos sencillos:
Transformada wavelet = promedio ponderado de series de precios utilizando un conjunto de “coeficientes wavelet”
Fórmula básica:
Suavizar los precios[i] = Σ(precio original)[i-j] × coeficientes wavelet[j]) / Σ(coeficientes wavelet)[j])
Perspectiva del filtro:
El precio original se filtra a través de un filtro wavelet → se “seleccionan” componentes de diferentes frecuencias.
La clave esSelección de coeficientes wavelet:
Por ejemplo:
Suponiendo que se utilizan las 4 wavelets de Daubechies, los coeficientes son[0.483, 0.837, 0.224, -0.129]:
Este conjunto de coeficientes define un filtro:
La transformada wavelet se completa al deslizar este filtro por toda la serie de precios. Cada deslizamiento implica cálculos.Promedio ponderado de precios dentro de la ventana actualLos pesos son los coeficientes wavelet.
¿Por qué puede “descomponer” señales?
Porque se puede demostrar matemáticamente que:Cualquier señal puede representarse como una combinación lineal de funciones base wavelet.Así como cualquier color se puede crear mezclando los tres colores primarios del RGB, cualquier serie de precios se puede derivar combinando funciones base de wavelets. Los diferentes tipos de wavelets proporcionan diferentes bibliotecas de funciones base, adecuadas para distintos tipos de análisis de señales.
En los libros de texto sobre procesamiento de señales, la transformada wavelet generalmente implica…Descomposición multinivel, reconstrucción y eliminación de ruido de umbralPasos:
Flujo de trabajo completo de análisis de ondículas:
PeroAplicaciones prácticas de las transacciones financierasEn China, no necesitamos que sea tan complicado. Porque:
1. Para operar sólo es necesario conocer la dirección de la tendencia; no es necesaria una reconstrucción perfecta.
La investigación académica puede requerir un error de reconstrucción inferior al 0,01 %, pero en el trading, es suficiente para determinar si el precio subirá o bajará. Incluso con un error del 5 %, la estrategia puede ser rentable siempre que la dirección de la tendencia sea correcta.
2. Los requisitos en tiempo real simplifican los cálculos.
Una descomposición completa de wavelets requiere el cálculo recursivo de múltiples capas de coeficientes, lo que puede causar latencia en el trading de alta frecuencia. La convolución directa, por otro lado, puede completarse en milisegundos, lo que satisface las necesidades del trading en vivo.
3. Las características especiales de las señales financieras
Los precios financieros no son señales estables ni presentan una ciclicidad estricta. La descomposición compleja de frecuencias no es muy útil en este caso; la simple extracción de tendencias resulta más práctica.
Por lo tanto, este artículoExtrayendo la esencia de la transformada waveletCentrándose en los aspectos más prácticos de los mercados financieros:
Simplificación básica 1: utilice únicamente coeficientes aproximados (tendencias de baja frecuencia)
Descomposición wavelet tradicional → coeficientes de aproximación + coeficientes de detalle (multicapa) Esta aplicación: Solo conserva coeficientes aproximados → obtiene directamente la tendencia suavizada.
Simplificación básica 2: convolución directa, sin eliminación de ruido mediante umbral
Descomposición wavelet tradicional → umbralización de coeficientes de detalle → reconstrucción Esta aplicación: Convolución directa → para obtener precios suavizados
Simplificación básica 3: Ignorar el procesamiento de límites
Las wavelets tradicionales requieren un procesamiento como la continuación simétrica o periódica de los límites de la señal. Esta aplicación se centra únicamente en la sección central; se aceptan errores de límites.
Método de implementación: convolución de filtro
def convolve(src, coeffs, step):
"""
核心算法:用小波系数对价格序列做加权平均
src: 价格序列 [100000, 101000, 99000, ...]
coeffs: 小波系数 [0.483, 0.837, 0.224, -0.129]
step: 采样步长(用于多层级)
"""
sum_val = 0.0 # 加权和
sum_w = 0.0 # 权重和
for i, weight in enumerate(coeffs):
idx = i * step
if idx < len(src):
sum_val += src[idx] * weight
sum_w += weight
return sum_val / sum_w # 归一化
Esta función es…El núcleo de los filtros wavelet:
stepLos parámetros permiten el suavizado de múltiples niveles (Nivel 1/2/3…).¿Por qué es razonable esta simplificación?
Porque el requisito esencial de las transacciones es:Encontrar tendencias en el ruidoLos coeficientes de aproximación de la transformada wavelet son en sí mismos un “filtro de paso bajo” para la señal, que preserva los componentes de tendencia de baja frecuencia, que es exactamente lo que necesitamos.
Si bien el análisis wavelet completo es más preciso, en el trading financiero:
usarEl motor de backtesting local de la plataforma Inventor Quantization (FMZ)¡Es muy conveniente obtener datos!
'''backtest
start: 2025-12-17 00:00:00
end: 2025-12-23 08:00:00
period: 1h
basePeriod: 1h
exchanges: [{"eid":"Futures_Binance","currency":"BTC_USDT","fee":[0,0]}]
'''
from fmz import *
task = VCtx(__doc__)
def main():
exchange.SetCurrency("BTC_USDT")
exchange.SetContractType("swap")
records = exchange.GetRecords(PERIOD_H1, 500)
return records
records = main()
No se requiere una compleja integración de API ni limpieza de datos; se pueden obtener directamente datos de velas estandarizadas. Esto nos permite verificar rápidamente los efectos reales de siete tipos de wavelets, en lugar de atascarnos en el procesamiento de datos.
Al comparar el rendimiento de siete tipos de wavelets comunes (Haar, Daubechies 4, Symlet 4, Biorthogonal 3.3, Mexican Hat, Morlet y Discrete Meyer) en los precios de las criptomonedas, se proporciona la siguiente demostración visual:
El enfoque no está en el rigor de la derivación matemática, sino en la comparación visual de los resultados prácticos.Esto ayuda a los traders a desarrollar una comprensión intuitiva y elegir el tipo de wavelet que se adapta a su estrategia.
La ondícula de Haar es el tipo más básico de ondícula, con solo dos coeficientes:[0.5, 0.5]Básicamente es un promedio simple de dos precios adyacentes.
Código central:
coeffs = [0.5, 0.5]
# 对价格序列 [100000, 101000, 99000, 102000, 98000] 处理
def smooth(prices, i):
return (prices[i] * 0.5 + prices[i-1] * 0.5) / 1.0
# 结果:[100000, 100500, 100000, 100500, 100000]
Como puede observar, el precio inicialmente fluctuante (de 99 000 a 102 000) se vuelve relativamente estable tras el procesamiento Haar. Este es el efecto de la eliminación de ruido de wavelets, que suaviza las fluctuaciones violentas a corto plazo, lo que permite observar una tendencia de precios más uniforme.

Daubechies 4 (db4 para abreviar) es una de las wavelets más utilizadas en ingeniería. Sus coeficientes son:[0.483, 0.837, 0.224, -0.129]Nótese que el último coeficiente es…número negativoEso es lo que lo hace único.
Código central:
coeffs = [0.483, 0.837, 0.224, -0.129]
# 处理第i个价格点
def smooth(prices, i):
weighted_sum = (prices[i] * 0.483 + # 当前价格
prices[i-1] * 0.837 + # 前1根,权重最大!
prices[i-2] * 0.224 + # 前2根
prices[i-3] * (-0.129)) # 前3根,负权重
weight_sum = 0.483 + 0.837 + 0.224 + (-0.129) # = 1.415
return weighted_sum / weight_sum
# 示例:smooth([100000, 101000, 99000, 102000], 3) ≈ 100251
Características principales:El peso de la vela anterior (0,837) es mayor que el del precio actual (0,483). Esto significa que db4 prioriza el precio que acaba de ocurrir, y el coeficiente de ponderación negativo compensará el precio anterior, mejorando aún más la fluidez.

Symlet 4 es una versión mejorada de Daubechies, que busca una mayor simetría. Coeficientes:[-0.076, -0.030, 0.498, 0.804, 0.298, -0.099, -0.013, 0.032]。
Código central:
coeffs = [-0.076, -0.030, 0.498, 0.804, 0.298, -0.099, -0.013, 0.032]
# 向前看8根K线
def smooth(prices, i):
weighted_sum = sum(prices[i-j] * coeffs[j] for j in range(8))
weight_sum = sum(coeffs)
return weighted_sum / weight_sum
# 平滑效果比Haar和db4都强,但反应速度更慢
Características principales:Una ventana de 8 velas permite una mayor memoria de los precios. Una verdadera inversión de tendencia podría no ser visible en una curva suave hasta después de 8 velas.

Biortogonal 3.3 (abreviado como bior3.3) es una ondícula perfectamente simétrica con coeficientes:[-0.066, 0.283, 0.637, 0.283, -0.066]。
Código central:
coeffs = [-0.066, 0.283, 0.637, 0.283, -0.066]
# ↑ 中心↑ ↑
# 完全对称的两端
# 处理中间价格点
def smooth(prices, i):
# 实际应用:只向前看,不使用未来数据
weighted_sum = (prices[i-4] * (-0.066) + # 前4根
prices[i-3] * 0.283 + # 前3根
prices[i-2] * 0.637 + # 前2根,权重最大
prices[i-1] * 0.283 + # 前1根
prices[i] * (-0.066)) # 当前
weight_sum = sum(coeffs) # = 1.071
return weighted_sum / weight_sum
Características principales:La simetría garantiza que no haya “distorsión de fase”: la curva suavizada no se desplazará hacia la izquierda o hacia la derecha inexplicablemente.

Coeficientes de sombrero mexicano (también llamado wavelet de Ricker):[-0.1, 0.0, 0.4, 0.8, 0.4, 0.0, -0.1]Tiene forma de sombrero mexicano.
Código central:
coeffs = [-0.1, 0.0, 0.4, 0.8, 0.4, 0.0, -0.1]
# 负值 零 正值 最大 正值 零 负值
# ↓ ↓
# "惩罚"两端,增强拐点检测能力
def smooth(prices, i):
weighted_sum = (prices[i-6] * (-0.1) + # 左3,负权重
prices[i-5] * 0.0 + # 左2
prices[i-4] * 0.4 + # 左1
prices[i-3] * 0.8 + # 中心,权重最大
prices[i-2] * 0.4 + # 右1
prices[i-1] * 0.0 + # 右2
prices[i] * (-0.1)) # 右3,负权重
weight_sum = sum(coeffs)
return weighted_sum / weight_sum
Características principales:Su estructura “grande en el medio, negativa en ambos extremos” lo hace particularmente bueno en la detección.punto de inflexiónEl momento crítico en el que los precios pasan de una tendencia alcista a una bajista (o viceversa). El coeficiente de ponderación negativo penaliza los precios distantes, captando rápidamente los cambios de tendencia.

La ondícula de Morlet se basa en una distribución gaussiana (normal), con coeficientes:[0.0625, 0.25, 0.375, 0.25, 0.0625]。
Código central:
coeffs = [0.0625, 0.25, 0.375, 0.25, 0.0625]
# ↓ ↓ ↓中心 ↓ ↓
# 远端 近端 最高 近端 远端
# 完美的高斯钟形曲线
def smooth(prices, i):
weighted_sum = (prices[i-4] * 0.0625 + # 左2,6.25%
prices[i-3] * 0.25 + # 左1,25%
prices[i-2] * 0.375 + # 中心,37.5%
prices[i-1] * 0.25 + # 右1,25%
prices[i] * 0.0625) # 右2,6.25%
# 权重和正好 = 1.0,无需除法
return weighted_sum
Características principales:La wavelet más suave de todas, no tiene ponderaciones negativas y todos los precios se incorporan gradualmente al cálculo. La curva resultante es extremadamente suave, pero el costo es una respuesta lenta: los cambios repentinos de precio pueden no reflejarse hasta varias velas después.

La wavelet discreta de Meyer es la wavelet más compleja, con coeficientes:[-0.015, -0.025, 0.0, 0.28, 0.52, 0.28, 0.0, -0.025, -0.015]。
Código central:
coeffs = [-0.015, -0.025, 0.0, 0.28, 0.52, 0.28, 0.0, -0.025, -0.015]
# ↑ ↑ ↑ ↑中心↑ ↑ ↑ ↑
# 完全对称,中心权重超过50%
def smooth(prices, i):
# 向前看9根K线
weighted_sum = sum(prices[i-j] * coeffs[j] for j in range(9))
weight_sum = sum(coeffs) # = 1.0
return weighted_sum
# 注意:第4根之前的K线权重是0.52,超过50%!
# 实际上在告诉你"4根K线之前的中期趋势"
Características principales:Tiene la mayor cantidad de coeficientes (9), los datos históricos más extensos y el efecto de suavizado más potente. Es adecuado para extraer tendencias semanales, pero presenta un gran retraso: incluso si el precio ha caído un 10%, su curva puede seguir mostrando una tendencia alcista continua.

Después de revisar los siete tipos de wavelets, deberías haber notado un patrón:
Más coeficientes = ver más = suavizado más fuerte = mayor latencia
Haar (2 coeficientes) → Solo mira 1 barra → Casi no es suave Daubechies 4 (4 piezas) → Ver 3 antes → Ligeramente liso Sombrero Mexicano (7) → Ver 6 antes → Suavizado moderado Meyer discreto (9) → Antes de ver 8 barras → Suavizado intenso
El efecto de los pesos negativos es mejorar la sensibilidad y facilitar la detección de cambios.
Haar/Morlet (sin pesos negativos) → Suave y liso, insensible Sombrero Mexicano (negativo en ambos extremos) → Sensible a los puntos de inflexión Daubechies 4 (negativo) → Sensible a los cambios de tendencia
El papel de la simetría = sin distorsión = manteniendo la forma original
Asimetría (Daubechies) → Puede desplazarse hacia la izquierda o hacia la derecha Simetría (Biortogonal/Meyer) → Mantenimiento de la posición central
La transformada wavelet se puede aplicar recursivamente, como muñecas anidadas. La primera aplicación se denomina Nivel 1, su aplicación al resultado del Nivel 1 se denomina Nivel 2, y así sucesivamente.
Las escalas de tiempo vistas en diferentes niveles:
Suponiendo que utilizamos gráficos de velas de 1 hora para operar con BTC:
Nivel 1 → Observar fluctuaciones a corto plazo durante 2 a 4 horas Nivel 2 → Observar la tendencia durante 4-8 horas Nivel 3 → Observar la tendencia a mediano plazo durante 1-2 días (estrategia de uso común) Nivel 4 → Observar el rango de precios de 2 a 4 días Nivel 5 → Observar las principales tendencias durante 4-8 días
Comparación de resultados reales:
Precio original de BTC (gráfico de 1 hora):99500, 99800, 99200, 100200, 99800, 100500, 100100, ...
Procesamiento de nivel 1: 99600, 99650, 99500, 99900, 99950, 100200, 100250, … (Ligeramente suavizado, pero aún son visibles las fluctuaciones)
Procesamiento de nivel 3: 99620, 99650, 99700, 99800, 99950, 100100, 100200, … (Suavizado, indicando una tendencia a mediano plazo)
Procesamiento de nivel 5: 99630, 99640, 99660, 99700, 99760, 99840, 99930, … (Extremadamente suave, solo muestra la dirección general)

El principio de selección es simple: utilice el nivel correspondiente en función de su período de tenencia.
Scalping de 15 minutos → Nivel 1-2
Negociación intradía → Nivel 2-3
Haz unos días de swing → Nivel 3-4
Análisis de tendencias a largo plazo → Nivel 4-5
La aplicación de la transformada wavelet en el trading es muy directa: se utiliza la curva de precios suavizada que genera para determinar la dirección de la tendencia y operar cuando esta cambia. En concreto, si el precio de cierre suavizado es superior al anterior, indica una tendencia alcista, por lo que se debe abrir una posición larga; si el precio de cierre suavizado es inferior al anterior, indica una tendencia bajista, por lo que se debe cerrar la posición o abrir una posición corta. Esta lógica es eficaz porque las wavelets filtran las fluctuaciones aleatorias a corto plazo, dejando una alta probabilidad de un cambio de tendencia real en el “subidón” o el “bajón”, en lugar de una señal falsa causada por el ruido.
# 执行小波变换
transformed = transformer.transform_ohlc(df)
# 获取最近两根K线的平滑收盘价
w_close_current = transformed['w_close'].values[-1] # 当前平滑收盘价
w_close_prev = transformed['w_close'].values[-2] # 前一根平滑收盘价
# 判断趋势方向
signal = 0
if w_close_current > w_close_prev:
signal = 1 # 平滑价格向上 → 做多
elif w_close_current < w_close_prev:
signal = -1 # 平滑价格向下 → 做空
# 获取账户信息
account = exchange.GetAccount()
ticker = exchange.GetTicker()
if not account or not ticker:
Log("[Warning] Failed to get account/ticker info")
Sleep(5000)
continue
current_price = ticker['Last']
Log(f"[Price] 原始: {df['Close'].values[-1]:.2f}, "
f"平滑当前: {w_close_current:.2f}, 平滑前值: {w_close_prev:.2f}")
Log(f"[Trend] {'↑ 向上' if signal == 1 else '↓ 向下' if signal == -1 else '→ 横盘'}")
# 执行交易逻辑
if signal == 1 and position != 1:
# 平滑价格向上 → 做多
Log(f"[信号] 趋势向上,开多 @ {current_price:.2f}")
if position == -1:
# 先平空仓
exchange.SetDirection("closesell")
exchange.Buy(current_price, 1)
Log(f"[平仓] 平空仓")
# 开多仓
exchange.SetDirection("buy")
exchange.Buy(current_price, 1)
Log(f"[开仓] 开多仓")
position = 1
elif signal == -1 and position != -1:
# 平滑价格向下 → 做空
Log(f"[信号] 趋势向下,开空 @ {current_price:.2f}")
if position == 1:
# 先平多仓
exchange.SetDirection("closebuy")
exchange.Sell(current_price, 1)
Log(f"[平仓] 平多仓")
# 开空仓
exchange.SetDirection("sell")
exchange.Sell(current_price, 1)
Log(f"[开仓] 开空仓")
position = -1
else:
Log(f"[持仓] 当前{'多头' if position == 1 else '空头' if position == -1 else '空仓'},无需操作")

Por supuesto, en la práctica, no es tan sencillo. Puedes usar varios niveles de wavelets simultáneamente, como el Nivel 2 que muestra la tendencia a corto plazo y el Nivel 4 que muestra la tendencia a largo plazo. Solo abre una posición cuando ambos se muevan en la misma dirección, lo que reduce significativamente las señales falsas. También puedes agregar otras condiciones de filtrado, como requerir un mayor volumen de negociación, una volatilidad suficientemente alta o una ruptura del precio por encima de un nivel clave; todas estas pueden mejorar la tasa de ganancia. Para las órdenes de stop-loss, puedes establecerlas dinámicamente usando el rango de fluctuación de precio suavizado por wavelets; por ejemplo, stop-loss si el precio cae por debajo del precio suavizado menos 2 veces el ATR. En la gestión de posiciones, cuanto más obvia sea la tendencia (cuanto más pronunciada sea la pendiente del precio suavizado), mayor será el tamaño de la posición; si la tendencia no es clara, usa una posición más ligera o mantente al margen.
Pero la idea central sigue siendo la misma: usar wavelets para transformar precios con fluctuaciones en tendencias claras y, a partir de ellas, emitir juicios. Esto es mucho más fiable que observar directamente las subidas y bajadas en el gráfico de velas original, ya que este podría subir un 3 % hoy, bajar un 2 % mañana y subir un 4 % al día siguiente. Simplemente no se puede distinguir si se trata de una tendencia o de una fluctuación. La curva procesada por wavelets indicará que «la tendencia general durante este período es alcista, aunque hay fluctuaciones intermedias».
En términos de efectos prácticos de suavizado, la transformada wavelet puede desempeñar un papel importante en el procesamiento de datos financieros: puede filtrar ruido a corto plazo y ayudar a extraer información de tendencias relativamente clara. Sin embargo, esta técnica también presenta limitaciones significativas.El problema del retraso no se puede evitar por completo.Sin embargo, solo puede procesar datos históricos y no puede predecir tendencias futuras. Además, el efecto del uso exclusivo de la transformada wavelet es relativamente limitado; debe combinarse con otros métodos analíticos y medidas de control de riesgos para construir un sistema de negociación completo.
La razón principal de esta limitación reside en la naturaleza única de los mercados financieros. En campos tradicionales de procesamiento de señales, como el reconocimiento de voz y el procesamiento de imágenes, las características del ruido son relativamente estables y los patrones de señal tienden a repetirse. Por lo tanto, la transformada wavelet puede separar eficazmente las señales del ruido. Sin embargo, los mercados financieros son completamente diferentes: las fluctuaciones que hoy se consideran “ruido” pueden convertirse en “señales” que reflejen los cambios del mercado mañana; los modelos analíticos que son eficaces ahora pueden volverse ineficaces en el futuro.El mercado en sí no es estacionario y cambia dinámicamente.No existen leyes inmutables, lo que requiere que la aplicación de la transformada wavelet en el campo financiero deba ajustarse de manera flexible de acuerdo con el entorno de mercado específico.
Cuando vea a alguien exagerar los efectos reales de la transformada wavelet y la transformada de Fourier, intente hacerse estas preguntas: ¿Qué tipo de wavelet se utilizó? ¿En qué se basó la elección de este tipo en lugar de otros? ¿Cómo se estableció el nivel de suavizado? ¿Existen resultados de backtesting y procedimientos de selección de parámetros correspondientes?Aquellos que realmente poseen conocimientos profesionales podrán explicar claramente estos detalles técnicos clave.
Basándonos en nuestro conocimiento limitado, realizamos esta exploración práctica.La idea central es compartir los conceptos de aplicación de la transformada wavelet de una manera sencilla y fácil de entender.Este artículo pretende ayudar a los lectores a comprender los fundamentos de esta tecnología. Valoramos mucho a los investigadores cuantitativos que trabajan en este campo. Si usted es un experto en este campo, le invitamos a señalar cualquier deficiencia del artículo, como la base teórica para la selección de parámetros wavelet, los métodos de optimización para combinaciones multiescala y las rutas de implementación para la selección adaptativa de wavelets. Aceptaremos con humildad sus sugerencias y mejoraremos continuamente el contenido.
Función de trazado: se aplica en el motor de backtesting local del inventor
'''backtest
start: 2025-12-17 00:00:00
end: 2025-12-23 08:00:00
period: 1h
basePeriod: 1h
exchanges: [{"eid":"Futures_Binance","currency":"BTC_USDT","fee":[0,0]}]
'''
from fmz import *
import numpy as np
import pandas as pd
import matplotlib.pyplot as plt
from matplotlib.patches import Rectangle
task = VCtx(__doc__)
# ==================== 小波系数库 ====================
class WaveletCoefficients:
"""Wavelet Coefficients Definition"""
@staticmethod
def get_coeffs(wavelet_name):
"""Get coefficients for different wavelet types"""
coeffs = {
"Haar": [0.5, 0.5],
"Daubechies 4": [
0.48296291314453414,
0.8365163037378079,
0.22414386804201339,
-0.12940952255126037
],
"Symlet 4": [
-0.05357, -0.02096, 0.35238,
0.56833, 0.21062, -0.07007,
-0.01941, 0.03268
],
"Biorthogonal 3.3": [
-0.06629, 0.28289, 0.63678,
0.28289, -0.06629
],
"Mexican Hat (Ricker)": [
-0.1, 0.0, 0.4, 0.8, 0.4, 0.0, -0.1
],
"Morlet (Gaussian)": [
0.0625, 0.25, 0.375, 0.25, 0.0625
],
"Discrete Meyer (Dmey)": [
-0.015, -0.025, 0.0,
0.28, 0.52, 0.28,
0.0, -0.025, -0.015
]
}
return coeffs.get(wavelet_name, coeffs["Mexican Hat (Ricker)"])
# ==================== 小波变换引擎 ====================
class WaveletTransform:
"""Wavelet Transform Engine"""
def __init__(self, wavelet_type="Mexican Hat (Ricker)", smoothing_level=3):
self.wavelet_type = wavelet_type
self.smoothing_level = smoothing_level
self.coeffs = WaveletCoefficients.get_coeffs(wavelet_type)
def convolve(self, src, coeffs, step):
"""
Convolution operation - Core algorithm
Args:
src: Source data sequence
coeffs: Wavelet coefficients
step: Sampling step
Returns:
Convolved value
"""
sum_val = 0.0
sum_w = 0.0
for i, weight in enumerate(coeffs):
idx = i * step
if idx < len(src):
val = src[idx]
sum_val += val * weight
sum_w += weight
# Normalization - Critical fix
return sum_val / sum_w if sum_w != 0 else sum_val
def calc_level(self, data, target_level):
"""
Calculate wavelet transform for specified level
Args:
data: Original data array
target_level: Target smoothing level
Returns:
Transformed data array
"""
result = []
coeffs = self.coeffs
for i in range(len(data)):
# Get data from current position backwards
src = data[max(0, i - 50):i + 1][::-1]
# Level 1
val = self.convolve(src, coeffs, 1)
# Level 2
if target_level >= 2:
src_temp = [val] + [self.convolve(data[max(0, j - 50):j + 1][::-1], coeffs, 1)
for j in range(max(0, i - 10), i)][::-1]
val = self.convolve(src_temp, coeffs, 2)
# Level 3
if target_level >= 3:
val = self.convolve([val] * len(coeffs), coeffs, 4)
# Level 4+
if target_level >= 4:
val = self.convolve([val] * len(coeffs), coeffs, 8)
result.append(val)
return np.array(result)
def transform_ohlc(self, df):
"""
Perform wavelet transform on OHLC data
Args:
df: DataFrame with Open/High/Low/Close
Returns:
Transformed DataFrame
"""
result_df = df.copy()
# Transform each price series
result_df['w_open'] = self.calc_level(df['Open'].values, self.smoothing_level)
result_df['w_high'] = self.calc_level(df['High'].values, self.smoothing_level)
result_df['w_low'] = self.calc_level(df['Low'].values, self.smoothing_level)
result_df['w_close'] = self.calc_level(df['Close'].values, self.smoothing_level)
# Reconstruct logically consistent candlesticks
result_df['real_high'] = result_df[['w_high', 'w_low', 'w_open', 'w_close']].max(axis=1)
result_df['real_low'] = result_df[['w_high', 'w_low', 'w_open', 'w_close']].min(axis=1)
return result_df
# ==================== K线图可视化工具 ====================
class WaveletCandlestickVisualizer:
"""Wavelet Candlestick Visualization"""
@staticmethod
def plot_single_wavelet(df, wavelet_type, smoothing_level=3, n_bars=200):
"""
Plot single wavelet type comparison
Args:
df: Original candlestick data
wavelet_type: Wavelet type
smoothing_level: Smoothing level
n_bars: Number of bars to display
"""
# Take only last n_bars
df_plot = df.iloc[-n_bars:].copy()
# Create figure
fig, ax = plt.subplots(figsize=(20, 8))
# Perform wavelet transform
transformer = WaveletTransform(wavelet_type, smoothing_level)
transformed = transformer.transform_ohlc(df)
transformed_plot = transformed.iloc[-n_bars:].copy()
# Draw original candlesticks (gray background)
WaveletCandlestickVisualizer._draw_candlesticks(
ax, df_plot,
color_up='lightgray',
color_down='lightgray',
alpha=0.3,
label='Original Candles'
)
# Draw wavelet smoothed candlesticks
WaveletCandlestickVisualizer._draw_candlesticks(
ax, transformed_plot,
use_wavelet=True,
color_up='#26A69A', # Green
color_down='#EF5350', # Red
alpha=0.9,
linewidth=1.2,
label=f'{wavelet_type} Smoothed (Level {smoothing_level})'
)
# Set title and labels
ax.set_title(f'{wavelet_type} Wavelet (Level {smoothing_level}) - Candlestick Comparison',
fontsize=16, fontweight='bold', pad=20)
ax.set_ylabel('Price (USDT)', fontsize=13)
ax.set_xlabel('Time', fontsize=13)
ax.grid(True, alpha=0.2, linestyle='--')
ax.legend(loc='upper left', fontsize=12)
# Format x-axis
ax.set_xlim(-1, len(df_plot))
ax.set_xticks(range(0, len(df_plot), max(1, len(df_plot) // 10)))
ax.set_xticklabels([df_plot.index[i].strftime('%m-%d %H:%M')
for i in range(0, len(df_plot), max(1, len(df_plot) // 10))],
rotation=45, ha='right')
plt.tight_layout()
plt.show()
return fig
@staticmethod
def plot_single_level(df, wavelet_type, level, n_bars=200):
"""
Plot single smoothing level
Args:
df: Original candlestick data
wavelet_type: Wavelet type
level: Smoothing level
n_bars: Number of bars to display
"""
# Take only last n_bars
df_plot = df.iloc[-n_bars:].copy()
# Create figure
fig, ax = plt.subplots(figsize=(20, 8))
# Perform wavelet transform
transformer = WaveletTransform(wavelet_type, level)
transformed = transformer.transform_ohlc(df)
transformed_plot = transformed.iloc[-n_bars:].copy()
# Draw original candlesticks
WaveletCandlestickVisualizer._draw_candlesticks(
ax, df_plot,
color_up='lightgray',
color_down='lightgray',
alpha=0.3,
label='Original Candles'
)
# Draw wavelet smoothed candlesticks
WaveletCandlestickVisualizer._draw_candlesticks(
ax, transformed_plot,
use_wavelet=True,
color_up='#26A69A',
color_down='#EF5350',
alpha=0.9,
linewidth=1.2,
label=f'Level {level} Smoothed'
)
# Set title and labels
ax.set_title(f'{wavelet_type} - Smoothing Level {level} Effect',
fontsize=16, fontweight='bold', pad=20)
ax.set_ylabel('Price (USDT)', fontsize=13)
ax.set_xlabel('Time', fontsize=13)
ax.grid(True, alpha=0.2, linestyle='--')
ax.legend(loc='upper left', fontsize=12)
# Format x-axis
ax.set_xlim(-1, len(df_plot))
ax.set_xticks(range(0, len(df_plot), max(1, len(df_plot) // 10)))
ax.set_xticklabels([df_plot.index[i].strftime('%m-%d %H:%M')
for i in range(0, len(df_plot), max(1, len(df_plot) // 10))],
rotation=45, ha='right')
plt.tight_layout()
plt.show()
return fig
@staticmethod
def _draw_candlesticks(ax, df, use_wavelet=False, color_up='green',
color_down='red', alpha=1.0, linewidth=1.0, label=''):
"""
Draw candlestick chart
Args:
ax: Matplotlib axis
df: Data DataFrame
use_wavelet: Whether to use wavelet data
color_up: Up color
color_down: Down color
alpha: Transparency
linewidth: Line width
label: Legend label
"""
if use_wavelet:
opens = df['w_open'].values
highs = df['real_high'].values
lows = df['real_low'].values
closes = df['w_close'].values
else:
opens = df['Open'].values
highs = df['High'].values
lows = df['Low'].values
closes = df['Close'].values
for i in range(len(df)):
x = i
open_price = opens[i]
high_price = highs[i]
low_price = lows[i]
close_price = closes[i]
color = color_up if close_price >= open_price else color_down
# Draw wick
ax.plot([x, x], [low_price, high_price],
color=color, linewidth=linewidth, alpha=alpha)
# Draw body
height = abs(close_price - open_price)
bottom = min(open_price, close_price)
rect = Rectangle((x - 0.3, bottom), 0.6, height,
facecolor=color, edgecolor=color,
alpha=alpha, linewidth=linewidth)
ax.add_patch(rect)
# Add legend (only once)
if label:
ax.plot([], [], color=color_up, linewidth=3, alpha=alpha, label=label)
# ==================== 主函数 ====================
def main():
"""Main execution flow"""
exchange.SetCurrency("BTC_USDT")
exchange.SetContractType("swap")
# Get candlestick data
records = exchange.GetRecords(PERIOD_H1, 500)
# Convert to DataFrame
df = pd.DataFrame(records, columns=['Time', 'Open', 'High', 'Low', 'Close', 'Volume'])
df['Time'] = pd.to_datetime(df['Time'], unit='ms')
df.set_index('Time', inplace=True)
print(f"Data loaded: {len(df)} bars")
print(f"Time range: {df.index[0]} to {df.index[-1]}")
print(f"Price range: ${df['Low'].min():.2f} - ${df['High'].max():.2f}")
return df
# ==================== 执行绘图 ====================
try:
# Get candlestick data
kline = main()
print("\n" + "="*70)
print("Generating Wavelet Candlestick Charts (Each in Separate Window)...")
print("="*70)
# ========== Chart Series 1: Different Wavelet Types ==========
print("\n[Series 1] Comparing Different Wavelet Types")
print("-" * 70)
wavelet_types = [
"Haar",
"Daubechies 4",
"Symlet 4",
"Biorthogonal 3.3",
"Mexican Hat (Ricker)",
"Morlet (Gaussian)",
"Discrete Meyer (Dmey)" # ✅ 添加了 Discrete Meyer
]
for i, wavelet_type in enumerate(wavelet_types, 1):
print(f" Chart {i}/{len(wavelet_types)}: {wavelet_type}")
fig = WaveletCandlestickVisualizer.plot_single_wavelet(
kline,
wavelet_type=wavelet_type,
smoothing_level=3,
n_bars=150
)
# ========== Chart Series 2: Different Smoothing Levels ==========
print("\n[Series 2] Comparing Different Smoothing Levels")
print("-" * 70)
levels = [1, 2, 3, 4, 5]
for i, level in enumerate(levels, 1):
print(f" Chart {i}/5: Level {level}")
fig = WaveletCandlestickVisualizer.plot_single_level(
kline,
wavelet_type="Mexican Hat (Ricker)",
level=level,
n_bars=150
)
print("\n" + "="*70)
print("All charts generated successfully!")
print(f"Total charts: {len(wavelet_types) + len(levels)} ({len(wavelet_types)} wavelets + {len(levels)} levels)")
print("="*70)
except Exception as e:
print(f"Error: {str(e)}")
import traceback
print(traceback.format_exc())
finally:
print("\nStrategy testing completed.")
Funciones de transacción: Aplicadas en la plataforma Inventors
'''backtest
start: 2025-01-17 00:00:00
end: 2025-12-23 08:00:00
period: 1h
basePeriod: 1h
exchanges: [{"eid":"Futures_Binance","currency":"BTC_USDT","fee":[0,0]}]
'''
import numpy as np
import pandas as pd
# ==================== 小波系数库 ====================
class WaveletCoefficients:
"""与上部分函数一致"""
# ==================== 小波变换引擎 ====================
class WaveletTransform:
"""与上部分函数一致"""
def main():
"""小波交易主函数 - 基于平滑价格趋势"""
# ========== 配置参数 ==========
WAVELET_TYPE = "Mexican Hat (Ricker)" # 小波类型
SMOOTHING_LEVEL = 1 # 平滑阶数
# 初始化
exchange.SetCurrency("BTC_USDT")
exchange.SetContractType("swap")
Log(f"=" * 70)
Log(f"Wavelet Trend Following Strategy")
Log(f"Wavelet: {WAVELET_TYPE}, Level: {SMOOTHING_LEVEL}")
Log(f"Logic: 平滑收盘价向上→做多, 平滑收盘价向下→做空")
Log(f"=" * 70)
# 初始化小波变换器
transformer = WaveletTransform(WAVELET_TYPE, SMOOTHING_LEVEL)
# 持仓状态
position = 0 # 0: 无持仓, 1: 多头, -1: 空头
while True:
# 获取K线数据
records = exchange.GetRecords(PERIOD_H1, 500)
if not records:
Log("[Warning] Failed to get kline data")
Sleep(5000)
continue
df = pd.DataFrame(records, columns=['Time', 'Open', 'High', 'Low', 'Close', 'Volume'])
df['Time'] = pd.to_datetime(df['Time'], unit='ms')
df.set_index('Time', inplace=True)
# 执行小波变换
transformed = transformer.transform_ohlc(df)
# 获取最近两根K线的平滑收盘价
w_close_current = transformed['w_close'].values[-1] # 当前平滑收盘价
w_close_prev = transformed['w_close'].values[-2] # 前一根平滑收盘价
# 判断趋势方向
signal = 0
if w_close_current > w_close_prev:
signal = 1 # 平滑价格向上 → 做多
elif w_close_current < w_close_prev:
signal = -1 # 平滑价格向下 → 做空
# 获取账户信息
account = exchange.GetAccount()
ticker = exchange.GetTicker()
if not account or not ticker:
Log("[Warning] Failed to get account/ticker info")
Sleep(5000)
continue
current_price = ticker['Last']
Log(f"[Price] 原始: {df['Close'].values[-1]:.2f}, "
f"平滑当前: {w_close_current:.2f}, 平滑前值: {w_close_prev:.2f}")
Log(f"[Trend] {'↑ 向上' if signal == 1 else '↓ 向下' if signal == -1 else '→ 横盘'}")
# 执行交易逻辑
if signal == 1 and position != 1:
# 平滑价格向上 → 做多
Log(f"[信号] 趋势向上,开多 @ {current_price:.2f}")
if position == -1:
# 先平空仓
exchange.SetDirection("closesell")
exchange.Buy(current_price, 1)
Log(f"[平仓] 平空仓")
# 开多仓
exchange.SetDirection("buy")
exchange.Buy(current_price, 1)
Log(f"[开仓] 开多仓")
position = 1
elif signal == -1 and position != -1:
# 平滑价格向下 → 做空
Log(f"[信号] 趋势向下,开空 @ {current_price:.2f}")
if position == 1:
# 先平多仓
exchange.SetDirection("closebuy")
exchange.Sell(current_price, 1)
Log(f"[平仓] 平多仓")
# 开空仓
exchange.SetDirection("sell")
exchange.Sell(current_price, 1)
Log(f"[开仓] 开空仓")
position = -1
else:
Log(f"[持仓] 当前{'多头' if position == 1 else '空头' if position == -1 else '空仓'},无需操作")
Log(f"[账户] 余额: {account['Balance']:.2f}, 权益: {account['Equity']:.2f}")
Log("-" * 70)
Sleep(60000 * 60)