2
focar em
410
Seguidores

Desvendando as três técnicas essenciais de processamento de dados financeiros: O que exatamente é a Transformada Wavelet?

Criado em: 2025-12-25 14:35:55, atualizado em: 2026-01-09 11:16:40
comments   0
hits   189

[TOC]

Desvendando as três técnicas essenciais de processamento de dados financeiros: O que exatamente é a Transformada Wavelet?

📢 Armadura em Camadas

Este artigo éUma abordagem de divulgação científica sobre a Transformada Wavelet para negociação prática.O código é uma versão didática simplificada (omitindo etapas complexas como decomposição multinível, remoção de ruído por limiar e reconstrução por transformada inversa de wavelets padrão), mantendo apenas as ideias principais:Utilize coeficientes wavelet para realizar suavização multiescala nos preços e extrair informações sobre tendências.Adequado para desenvolvimento de estratégias e validação rápida, mas não para pesquisa acadêmica ou publicação de artigos.


I. Introdução: Desmascarando o “Guru Zhihu” (Especialista)

Quem costuma navegar por tópicos financeiros e quantitativos no Zhihu certamente já se deparou com este cenário:

Alguns “especialistas” continuam dizendo:

  • Redução de ruído por transformada wavelet
  • Período de extração da transformada de Fourier
  • O alisamento de Laplace remove valores discrepantes.

Ele deixou todos atônitos, como se tivesse dominado a arma nuclear do trading quantitativo.

Mas você quer que ele lhe mostre o código?
“Isto… é um segredo comercial e não posso divulgá-lo.”

Peça a ele para explicar o princípio.
“Isso… envolve matemática avançada, você não entenderia mesmo se eu lhe explicasse.”

Hoje, vamos explorar os tópicos frequentemente mencionados por esses “especialistas em Zhihu”, apresentar as aplicações práticas da transformada wavelet no mercado financeiro e ajudar a todos a desenvolver uma compreensão correta dessa tecnologia.


II. O que é exatamente a transformada wavelet?

Explicação simples

Imagine que você está ouvindo uma música, mas há ruído de fundo na gravação:

Gravação original = voz humana + ruído de fundo + estática

A transformada wavelet é como umaFiltros inteligentes

  • Preserve a voz
  • Filtrar ruído
  • Também pode indicar qual parte é o refrão e qual parte é o verso.

Mudar para o mercado financeiro:

Preço original = Tendência real + Flutuações de curto prazo + Ruído aleatório

A transformada wavelet pode te ajudar:

  • Extraindo a verdadeira tendência(Direção a longo prazo)
  • Filtrar flutuações de curto prazo(Flutuações intradiárias)
  • Identificação de pontos de inflexão importantes(Inversão de tendência)

Conceito central: Decomposição em funções de base

A essência da transformada wavelet éDecomponha o sinal original usando um conjunto específico de “funções de base” (wavelets).

Imagine que você queira descrever a aparência de uma pessoa:

  • Método tradicional: descrever pixel por pixel, o que é muito tedioso.
  • Método Wavelet: utiliza fatores como “tamanho dos olhos, altura do nariz e contorno facial”.recursoCombinar descrições

Em preços financeiros:

Série de preços original = Função base 1 × Peso 1 + Função base 2 × Peso 2 + … + Ruído

As funções de base são os “modelos” correspondentes aos coeficientes wavelet.Diferentes tipos de wavelet (Haar, Daubechies, Mexican Hat, etc.) usam modelos diferentes, assim como se usam diferentes “extratores de características” para decompor preços.

Filtro: Uma peneira no domínio da frequência

A transformada wavelet é essencialmente uma…banco de filtros multiescala:

Filtro de alta frequência → Captura flutuações rápidas (ruído diário, flutuações no nível do tick) Filtro de frequência intermediária → Captura tendências de médio prazo (faixas que variam de algumas horas a alguns dias). Filtros de baixa frequência → Capturam tendências de longo prazo (tendências semanais e mensais).

Por que é chamado de “wavelet”?

  • A transformada de Fourier tradicional utilizaOnda senoidal infinitamente longaComo uma régua infinitamente longa
  • A transformada wavelet é utilizada.Onda “pequena” de comprimento finitoComo um conjunto de réguas de comprimentos diferentes

O problema de usar ondas senoidais para analisar preços financeiros: as ondas senoidais pressupõem que o sinal se repete periodicamente, mas os mercados financeiros não! O BTC pode subir 10% hoje e cair 8% amanhã, sem qualquer ciclicidade.

Vantagens das wavelets:Análise de localizaçãoIsso pode indicar que “a tendência de preço foi principalmente de alta entre as 15h e as 17h do dia 20 de dezembro de 2025”, em vez de uma conclusão geral como “o mercado em geral estava oscilando”.

Reconstrução: da decomposição à reconstrução

A transformada wavelet éReversívelIsso é muito importante!

Preço original —> Decomposição wavelet —> Componente de tendência + Componente de volatilidade + Componente de ruído Componente de tendência + componente de volatilidade + componente de ruído —> reconstrução wavelet —> preço original

Processo de refatoraçãoConsiste em decompor os vários componentes.Combinar seletivamente de volta

  • Componente de tendência =[99800, 99850, 99900, 99950, …] # O que queremos
  • Componente de flutuação =[+200, -150, +180, -120, …] # Pode ser útil
  • Componente de ruído =[±10, ±15, ±8, ±12, …] # Descartar!

Utilize apenas componentes de tendência durante a reconstrução.

Após a decomposição, obteve-se o seguinte:

  • Preço reconstruído = Componente de tendência + Componente de volatilidade parcial

Em transações reais, geralmenteReconstrua apenas a parte de baixa frequência.(Tendência) Os componentes de alta frequência (ruído) são descartados diretamente. Este é o princípio da “redução de ruído” por wavelet.

Princípios da Matemática (Versão Simplificada)

Vamos deixar de lado as fórmulas integrais complexas e explicar em termos simples:

Transformada wavelet = média ponderada de séries de preços usando um conjunto de “coeficientes wavelet”

Fórmula básica:

Suavização de preços[i] = Σ(preço original)[i-j] × coeficientes wavelet[j]) / Σ(coeficientes wavelet)[j])

Perspectiva do filtro

O preço original é filtrado através de um filtro wavelet → componentes de diferentes frequências são “selecionados”.

A chave éSeleção de coeficientes wavelet

  • Wavelets diferentes = características de filtro diferentes = respostas de frequência diferentes
  • Níveis diferentes = escalas de tempo diferentes = ciclos de tendência diferentes

Por exemplo

Supondo que você utilize as wavelets de Daubechies 4, os coeficientes são[0.483, 0.837, 0.224, -0.129]:

Este conjunto de coeficientes define um filtro:

  • Coeficientes positivos (0,483, 0,837, 0,224) → Manter o preço para essas posições.
  • Coeficiente negativo (-0,129) → Suprime movimentos de preço anteriores
  • A ponderação dos coeficientes determina a contribuição de cada preço.

A transformada wavelet é concluída quando você “desliza” esse filtro por toda a série de preços. Cada deslizamento envolve cálculos.Média ponderada dos preços dentro da janela atualOs pesos são os coeficientes wavelet.

Por que ele consegue “decompor” sinais?

Porque isso pode ser comprovado matematicamente:Qualquer sinal pode ser representado como uma combinação linear de funções de base wavelet.Assim como qualquer cor pode ser criada pela mistura das três cores primárias do RGB, qualquer série de preços pode ser derivada pela combinação de funções de base wavelet. Diferentes tipos de wavelet fornecem diferentes “bibliotecas de funções de base”, adequadas para diferentes tipos de análise de sinal.


III. Esta Tentativa: Aplicação Prática de 7 Transformadas Wavelet

Abordagem de aplicação: Simplificação da teoria à prática

Nos livros didáticos de processamento de sinais, a transformada wavelet normalmente envolve cálculos complexos…Decomposição multinível, reconstrução e redução de ruído por limiar.Passos:

Fluxo de trabalho completo para análise de wavelet:

  1. A decomposição multiescala produz coeficientes de aproximação e coeficientes de detalhe.
  2. Limiarização → Limiarização suave/rígida para coeficientes de detalhe para reduzir o ruído
  3. Reconstrução por transformada inversa → restauração dos coeficientes processados ​​ao sinal
  4. Extensão de limites → Tratamento de efeitos de limites de sinal
  5. Normalização energética → garante a conservação de energia antes e depois da transformação.

MasAplicações práticas de transações financeirasNa China, não precisamos que seja tão complicado. Porque:

1. Para operar no mercado, basta identificar a direção da tendência; não é necessário um planejamento perfeito.

Em pesquisas acadêmicas, pode ser necessário um erro de reconstrução inferior a 0,01%, mas no mercado financeiro, isso é suficiente para determinar se o preço subirá ou cairá. Mesmo que a reconstrução apresente um erro de 5%, a estratégia ainda pode ser lucrativa, desde que a direção da tendência esteja correta.

2. Requisitos em tempo real simplificam os cálculos.

A decomposição wavelet completa requer o cálculo recursivo de múltiplas camadas de coeficientes, o que pode causar latência em negociações de alta frequência. A convolução direta, por outro lado, pode ser concluída em milissegundos, atendendo às necessidades de negociações em tempo real.

3. As características especiais dos sinais financeiros

Os preços financeiros não são sinais estáveis ​​e não exibem ciclicidade estrita. A decomposição de frequência complexa não é muito significativa neste caso; a extração de tendência simples é mais prática.

Essa estratégia de simplificação

Portanto, este artigoExtraindo a essência da transformada waveletFocando nos aspectos mais práticos dos mercados financeiros:

Simplificação Essencial 1: Use apenas coeficientes aproximados (tendências de baixa frequência)

Decomposição wavelet tradicional → coeficientes de aproximação + coeficientes de detalhe (multicamadas) Esta aplicação: Reter apenas os coeficientes aproximados → obter diretamente a tendência suavizada.

Simplificação do núcleo 2: Convolução direta, sem limiarização para remoção de ruído

Decomposição wavelet tradicional → limiarização dos coeficientes de detalhe → reconstrução Esta aplicação: Convolução direta → para obter preços suavizados

Simplificação Essencial 3: Ignorar o Processamento de Fronteira

As wavelets tradicionais requerem processamento como extensão simétrica e extensão periódica dos limites do sinal. Esta aplicação foca-se apenas na secção central; erros nos limites são aceitáveis.

Método de implementação: Convolução 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 função é…O núcleo dos filtros wavelet

  • Para cada vela, são analisadas N velas anteriores (N = número de coeficientes wavelet).
  • Calcule a média ponderada usando os coeficientes wavelet como pesos.
  • AjustandostepOs parâmetros permitem o suavização em vários níveis (Nível 1/2/3…).

Por que essa simplificação é razoável?

Porque o requisito essencial das transações é:Identificando tendências no ruídoOs coeficientes de aproximação da transformada wavelet funcionam como um “filtro passa-baixa” para o sinal, preservando os componentes de tendência de baixa frequência, que é exatamente o que precisamos.

Embora a análise wavelet completa seja mais precisa, no mercado financeiro:

  • Os lucros provêm da direção da tendência.Não se trata da precisão da reconstrução.
  • Métodos mais simples são mais robustosModelos complexos são propensos a sobreajuste.
  • A velocidade de cálculo é mais importante.No mercado financeiro real, cada milissegundo é dinheiro.

Aquisição de dados: a conveniência da plataforma FMZ

usarO mecanismo de backtesting local da plataforma Inventor Quantization (FMZ)É muito conveniente obter dados!

'''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()

Não é necessária nenhuma integração complexa de API nem limpeza de dados; os dados padronizados de candlestick podem ser obtidos diretamente. Isso nos permite verificar rapidamente os efeitos reais dos sete tipos de wavelet, em vez de nos perdermos no processamento de dados.

Alvo de teste

Ao comparar o desempenho de sete tipos comuns de wavelet (Haar, Daubechies 4, Symlet 4, Biorthogonal 3.3, Mexican Hat, Morlet e Discrete Meyer) nos preços das criptomoedas, a seguinte demonstração visual é fornecida:

  • Diferenças na intensidade de suavização entre diferentes wavelets
  • Variações de efeito em diferentes níveis da mesma wavelet
  • Quais wavelets são adequadas para negociação de curto prazo e quais são adequadas para seguir tendências?

O foco não está no rigor da dedução matemática, mas na comparação visual dos resultados práticos.Isso ajuda os traders a desenvolverem uma compreensão intuitiva e a escolherem o tipo de wavelet mais adequado à sua estratégia.


IV. Explicação detalhada dos 7 tipos de wavelet

1. Wavelet de Haar - a média mais simples

A wavelet de Haar é o tipo mais básico de wavelet, com apenas dois coeficientes:[0.5, 0.5]Essencialmente, trata-se da média simples de dois preços adjacentes.

Código principal:

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 você pode ver, o preço, que inicialmente apresentava flutuações bruscas (de 99.000 a 102.000), torna-se relativamente estável após o processamento Haar. Este é o efeito da “remoção de ruído” por wavelet — suavizando flutuações violentas de curto prazo, permitindo visualizar uma tendência de preço mais suave.

Desvendando as três técnicas essenciais de processamento de dados financeiros: O que exatamente é a Transformada Wavelet?


2. Daubechies 4 - Comumente usado em projetos de engenharia

A wavelet Daubechies 4 (db4, para abreviar) é uma das wavelets mais utilizadas em engenharia. Seus coeficientes são:[0.483, 0.837, 0.224, -0.129]Observe que o último coeficiente é…número negativoÉ isso que o torna único.

Código principal:

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

Principais características:O peso do candle anterior (0,837) é maior que o do preço atual (0,483)! Isso significa que o db4 dá mais ênfase ao “preço que acabou de ocorrer”, e o coeficiente de ponderação negativo terá um efeito de “compensação” sobre o preço anterior, aprimorando ainda mais a suavidade.

Desvendando as três técnicas essenciais de processamento de dados financeiros: O que exatamente é a Transformada Wavelet?


3. Symlet 4 - Uma versão simétrica aprimorada

Symlet 4 é uma versão aprimorada do Daubechies, visando uma melhor simetria. Coeficientes:[-0.076, -0.030, 0.498, 0.804, 0.298, -0.099, -0.013, 0.032]

Código principal:

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都强,但反应速度更慢

Principais características:Uma janela de 8 velas permite uma “memória” mais longa dos preços. Uma verdadeira reversão de tendência pode não ser visível em uma curva suave até que 8 velas tenham passado.

Desvendando as três técnicas essenciais de processamento de dados financeiros: O que exatamente é a Transformada Wavelet?


4. Biorthogonal 3.3 - Simetria Perfeita

Biorthogonal 3.3 (abreviado como bior3.3) é uma wavelet perfeitamente simétrica com coeficientes:[-0.066, 0.283, 0.637, 0.283, -0.066]

Código principal:

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

Principais características:A simetria garante que não haja “distorção de fase” - a curva suavizada não se deslocará inexplicavelmente para a esquerda ou para a direita.

Desvendando as três técnicas essenciais de processamento de dados financeiros: O que exatamente é a Transformada Wavelet?


5. Chapéu Mexicano - Caçador de Pontos de Virada

Coeficientes da wavelet Mexican Hat (também chamada de wavelet de Ricker):[-0.1, 0.0, 0.4, 0.8, 0.4, 0.0, -0.1]Tem o formato de um sombrero mexicano.

Código principal:

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

Principais características:Sua estrutura “grande no meio e negativa em ambas as extremidades” a torna particularmente eficaz na detecção.ponto de inflexão- O momento crítico em que os preços revertem de uma tendência de alta para uma tendência de baixa (ou vice-versa). O coeficiente de ponderação negativo “penaliza” preços distantes, capturando rapidamente as mudanças de tendência.

Desvendando as três técnicas essenciais de processamento de dados financeiros: O que exatamente é a Transformada Wavelet?


6. Morlet - O Rei do Suavização Gaussiana

A wavelet de Morlet é baseada em uma distribuição gaussiana (normal), com coeficientes:[0.0625, 0.25, 0.375, 0.25, 0.0625]

Código principal:

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

Principais características:A mais “suave” de todas as wavelets, não possui pesos negativos e todos os preços são incorporados ao cálculo de forma gradual. A curva resultante é extremamente suave, mas o custo é uma resposta lenta – mudanças repentinas de preço podem não ser refletidas até vários candlesticks depois.

Desvendando as três técnicas essenciais de processamento de dados financeiros: O que exatamente é a Transformada Wavelet?


7. Meyer Discreto - Suavização Suprema

A wavelet de Meyer discreta é a wavelet mais complexa, com coeficientes:[-0.015, -0.025, 0.0, 0.28, 0.52, 0.28, 0.0, -0.025, -0.015]

Código principal:

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线之前的中期趋势"

Principais características:Possui o maior número de coeficientes (9), o histórico de dados mais extenso e o efeito de suavização mais forte. É adequado para extrair “tendências semanais”, mas apresenta um atraso considerável - mesmo que o preço tenha caído 10%, sua curva ainda pode mostrar “continuação da alta”.

Desvendando as três técnicas essenciais de processamento de dados financeiros: O que exatamente é a Transformada Wavelet?


5. Por que existem diferenças no efeito de suavização?

Após analisar os 7 tipos de wavelets, você deve ter percebido o padrão:

Mais coeficientes = veja mais detalhes = suavização mais forte = maior latência

Haar (2 coeficientes) → Observe apenas 1 barra → Quase não é suave Daubechies 4 (4 unidades) → Veja o item 3 anterior → Ligeiramente liso Chapéu Mexicano (7) → Veja 6 antes → Suavização moderada Meyer discreto (9) → Antes de visualizar 8 barras → Suavização pesada

O efeito dos pesos negativos é aumentar a sensibilidade e facilitar a detecção de mudanças.

Haar/Morlet (sem pesos negativos) → Suave e macio, insensível Chapéu Mexicano (negativo em ambas as extremidades) → Sensível a pontos de inflexão Daubechies 4 (negativo) → Sensível a mudanças de tendência

O papel da simetria = ausência de distorção = manutenção da forma original

Assimetria (Daubechies) → Pode deslocar-se para a esquerda/direita Simetria (Biorthogonal/Meyer) → Manter a posição central


VI. O impacto dos níveis de suavização

A transformada wavelet pode ser aplicada recursivamente, como bonecas aninhadas. A primeira aplicação é chamada de Nível 1, aplicá-la novamente ao resultado do Nível 1 é chamado de Nível 2, e assim por diante.

As escalas de tempo observadas em diferentes níveis:

Supondo que utilizemos gráficos de velas de 1 hora para negociação de BTC:

Nível 1 → Observe as flutuações de curto prazo ao longo de 2 a 4 horas. Nível 2 → Observe a tendência ao longo de 4 a 8 horas. Nível 3 → Observe a tendência de médio prazo ao longo de 1 a 2 dias (estratégia comumente utilizada) Nível 4 → Observe a faixa de preço de 2 a 4 dias Nível 5 → Observe as principais tendências ao longo de 4 a 8 dias.

Comparação de resultados reais:

Preço original do BTC (gráfico de 1 hora):99500, 99800, 99200, 100200, 99800, 100500, 100100, ...

Processamento de nível 1: 99600, 99650, 99500, 99900, 99950, 100200, 100250, … (Ligeiramente suavizado, mas as flutuações ainda são visíveis)

Processamento de nível 3: 99620, 99650, 99700, 99800, 99950, 100100, 100200, … (Suavizado, indicando uma tendência de médio prazo)

Processamento de nível 5: 99630, 99640, 99660, 99700, 99760, 99840, 99930, … (Extremamente suave, mostrando apenas a direção geral)

Desvendando as três técnicas essenciais de processamento de dados financeiros: O que exatamente é a Transformada Wavelet? Desvendando as três técnicas essenciais de processamento de dados financeiros: O que exatamente é a Transformada Wavelet? Desvendando as três técnicas essenciais de processamento de dados financeiros: O que exatamente é a Transformada Wavelet? Desvendando as três técnicas essenciais de processamento de dados financeiros: O que exatamente é a Transformada Wavelet? Desvendando as três técnicas essenciais de processamento de dados financeiros: O que exatamente é a Transformada Wavelet?

O princípio de seleção é simples: utilize o nível correspondente com base no seu período de investimento.

Corte de cabelo em 15 minutos → Nível 1-2
Negociação intraday → Nível 2-3
Faça alguns dias de swing → Nível 3-4
Análise de tendências de longo prazo → Nível 4-5


VII. Ideias de Aplicação Prática

A aplicação da transformada wavelet no trading é muito direta: utiliza-se a curva de preço suavizada que ela gera para determinar a direção da tendência e opera quando a tendência muda. Especificamente, se o preço de fechamento suavizado for maior que o anterior, isso indica uma tendência de alta, então abre-se uma posição de compra; se o preço de fechamento suavizado for menor que o anterior, isso indica uma tendência de baixa, então fecha-se a posição ou abre-se uma posição de venda. Essa lógica é eficaz porque as wavelets filtram as flutuações aleatórias de curto prazo, deixando “para cima” ou “para baixo” como sinais de alta probabilidade de uma mudança genuína de tendência, em vez de sinais falsos causados ​​por ruído.

# 执行小波变换
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 '空仓'},无需操作")

Desvendando as três técnicas essenciais de processamento de dados financeiros: O que exatamente é a Transformada Wavelet?

Claro que, na prática, não é tão simples. Você pode usar vários níveis de wavelets simultaneamente, como o Nível 2 mostrando a tendência de curto prazo e o Nível 4 mostrando a tendência de longo prazo. Abra uma posição somente quando ambos estiverem se movendo na mesma direção, o que reduz significativamente os sinais falsos. Você também pode adicionar outras condições de filtragem, como exigir um volume de negociação maior, volatilidade suficientemente alta ou uma ruptura de preço acima de um nível-chave; tudo isso pode melhorar a taxa de acerto. Para ordens de stop-loss, você pode defini-las dinamicamente usando a faixa de flutuação de preço suavizada por wavelet; por exemplo, stop-loss se o preço cair abaixo do preço suavizado menos 2 vezes o ATR. No gerenciamento de posição, quanto mais óbvia a tendência (quanto mais íngreme a inclinação do preço suavizado), maior o tamanho da posição; se a tendência não estiver clara, use uma posição menor ou fique de fora.

Mas a ideia central permanece a mesma: usar wavelets para transformar preços ruidosos em tendências claras e, em seguida, tomar decisões com base nessas tendências. Isso é muito mais confiável do que observar diretamente a alta e a queda no gráfico de velas original, porque o gráfico de velas original pode subir 3% hoje, cair 2% amanhã e subir 4% depois de amanhã. Simplesmente não é possível distinguir se é uma tendência ou uma flutuação. A curva processada por wavelets indicará que “a tendência geral durante este período é de alta, embora haja flutuações intermediárias”.


VIII. Conclusão: Uma visão racional da “abordagem tríplice”

Em termos de efeitos práticos de suavização, a transformada wavelet pode de fato desempenhar um papel no processamento de dados financeiros: ela pode filtrar alguns ruídos de curto prazo e ajudar a extrair informações de tendência relativamente claras. No entanto, essa técnica também apresenta limitações significativas—O problema do atraso não pode ser completamente evitado.No entanto, ela só consegue processar dados históricos e não pode prever tendências futuras. Além disso, o efeito do uso isolado da transformada wavelet é relativamente limitado; ela precisa ser combinada com outros métodos analíticos e medidas de controle de risco para construir um sistema de negociação completo.

A principal razão para essa limitação reside na natureza singular dos mercados financeiros. Em áreas tradicionais de processamento de sinais, como reconhecimento de fala e processamento de imagens, as características do ruído são relativamente estáveis ​​e os padrões de sinal tendem a se repetir. Portanto, a transformada wavelet pode separar eficazmente os sinais do ruído. Contudo, os mercados financeiros são completamente diferentes: flutuações consideradas “ruído” hoje podem se tornar “sinais” que refletem mudanças de mercado amanhã; modelos analíticos que são eficazes agora podem se tornar ineficazes no futuro.O próprio mercado não é estático e está em constante mudança.Não existem leis imutáveis ​​na transformada wavelet, o que exige que a aplicação da transformada wavelet no campo financeiro seja ajustada de forma flexível de acordo com o ambiente de mercado específico.

Quando você vir alguém exagerando os efeitos reais da transformada wavelet e da transformada de Fourier, tente fazer estas perguntas: Qual tipo de wavelet foi usado? Qual a justificativa para a escolha desse tipo em vez de outros? Como foi definido o nível de suavização? Existem resultados de backtesting e procedimentos de seleção de parâmetros correspondentes?Aqueles que realmente possuem conhecimento profissional serão capazes de explicar claramente esses detalhes técnicos essenciais.

Com base em nosso conhecimento limitado, realizamos esta exploração prática.A ideia central é compartilhar os conceitos de aplicação da transformada wavelet de uma maneira simples e fácil de entender.Este artigo tem como objetivo auxiliar os leitores a obterem uma compreensão básica dessa tecnologia. Prezamos muito os pesquisadores quantitativos profundamente envolvidos nessa área; se você é um especialista no assunto, sinta-se à vontade para apontar quaisquer lacunas no artigo — como a base teórica para a seleção de parâmetros wavelet, métodos de otimização para combinações multiescala e caminhos de implementação para a seleção adaptativa de wavelets. Aceitaremos suas sugestões com gratidão e continuaremos a aprimorar o conteúdo.

Função de plotagem: Aplicada no mecanismo de backtesting local do 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.")

Funções de transação: Aplicadas na 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)