自学内容网 自学内容网

借助小波变换信号,驾驭股价波动

作者:老余捞鱼

原创不易,转载请标明出处及原作者。

写在前面的话:
       
本文将介绍利用 Python 中的小波变换分析股票价格波动,并通过连续小波变换(CWT)提取特征,以识别潜在的买入和卖出信号。在 Python 实现部分,我会提供完整的代码示例,包括设置环境、获取股票数据、应用 CWT、提取买卖信号以及识别信号交叉点。此外,还告诉大家如何使用 matplotlib 库对买入和卖出信号进行可视化,并通过回溯测试优化小波参数以提高交易策略的性能。

       股票涨涨跌跌,形成的模式和走势就像海浪一样难以预测。然而,就像科学家通过了解洋流波动来预测海浪的运动一样,我们也可以用类似的工具来解读股市的一些规律。借助小波变换的威力,我们希望能透过现象看本质,探寻推动股价的深层因素。这不仅仅是数字与数据,而是要将抽象转化为具体,从股票看似无常的行为中找到其节奏与缘由。

一、小波变换理论

       就其核心而言,小波是一种短暂的振荡,其能量集中在时间上,确保了它的短暂性和持续时间的有限性。想象一下敲击鼓声:产生的声音很强烈,但很快就会消失。小波的这种短暂特性使其非常适合分析非稳态(即其统计特性随时间变化)的金融信号。小波变换就像一个显微镜,可以观察股票数据错综复杂的细节,捕捉其大的趋势和微小的波动。

1.1 为什么不选傅立叶变换?

       许多人可能会想,为什么不直接使用傅立叶变换这一非常流行的信号分析工具呢?傅立叶变换可将信号分解为正弦组成。然而,它的致命弱点是无法同时提供时间和频率信息。虽然它能告诉我们存在的频率,但往往忽略了这些频率出现的时间。与傅里叶变换不同,小波变换同时捕捉频率和时间,提供信号的时频表示。

1.2 小波变换的本质

       在数学上,小波变换可以表示为:

  • W 是我们比较的结果,为我们提供了相似度的度量。
  • f (t) 代表我们的股票数据。
  • ψ (t) 是选择的小波模式。

       实质上,我们是在股票数据 f(t) 上移动小波 ψ,寻找它们吻合的地方。这就得到了小波系数 W(b),它告诉我们股票数据在每个时间点与小波的匹配程度。简单地说,任何给定点的匹配度越高,W 的值就越大。

       在展示的可视化效果中,我们看到了小波变换应用于时间序列数据(如股票价格)的机制。左图是连续小波变换(CWT)应用于股票数据的生动写照。动画的每一帧都显示了不同尺度的小波系数,捕捉了不同时间跨度下存在的模式。较宽的小波检测较长期的趋势,而较窄的小波则关注短期波动。

              右侧是小波(本例中为 Ricker 小波)如何穿越股票信号的描述。当小波滑过时,它会与股票数据逐点相乘,然后将所得数值相加,得出一个单一的系数。下面绘制了这种 "相关性",红点表示小波移动时的当前系数值。相关性图中的高点表示小波与股票数据高度一致的位置,捕捉特定的模式。从本质上讲,这一过程是在庞大的时间序列数据中发掘局部模式的系统方法,提供了傅立叶变换可能忽略的细粒度视角。

二、用 Python 实现

现在,我们将深入探讨小波变换的 Python 实现,以便从股票数据中提取交易者和数据科学家可能感兴趣的买入/卖出信号。代码采用连续小波变换来提取股票历史数据中可指示潜在买入或卖出时刻的特征。

2.1 设置环境

       确保有所需的 Python 库。如果没有,可以通过 pip 安装:

pip install numpy pandas matplotlib yfinance scipy

2.2 获取库存数据

       使用雅虎财经 yfinance 库,我们可以轻松获取股票数据进行分析:

import yfinance as yf

ticker = "SIE.DE"
stock_data = yf.download(ticker, start="2018-01-01", end="2023-12-30")

2.3 应用连续小波变换 (CWT)

        scipy库提供了使用 Ricker 小波计算 CWT 所需的函数:

from scipy import signal

widths = np.arange(1, 15)
cwt_result = signal.cwt(stock_data['Close'].values, signal.ricker, widths)

2.4 提取买卖信号

       从计算出的 CWT 中可以提取正系数和负系数,分别表示股价的上涨和下跌。

cwt_positive = np.where(cwt_result > 0, cwt_result, 0)
cwt_negative = np.where(cwt_result < 0, cwt_result, 0)

buy_signal = pd.Series(np.sum(cwt_positive, axis=0), index=stock_data.index)
sell_signal = pd.Series(-np.sum(cwt_negative, axis=0), index=stock_data.index)

2.5 识别信号交叉点

       买入和卖出信号之间的交叉可能预示着交易机会。

cross_above = (buy_signal >= sell_signal) & (buy_signal.shift(1) < sell_signal.shift(1))
cross_below = (buy_signal <= sell_signal) & (buy_signal.shift(1) > sell_signal.shift(1))

2.6 可视化

       可视化表示法可以对照股票的历史数据,更清晰地显示买入/卖出时刻:

import matplotlib.pyplot as plt

plt.figure(figsize=(30, 6))
plt.plot(stock_data.index, stock_data['Close'], label='Close Prices', alpha=0.5)
plt.scatter(stock_data.index[cross_above], stock_data['Close'][cross_above], label='Buy Signal', marker='^', color='g')
plt.scatter(stock_data.index[cross_below], stock_data['Close'][cross_below], label='Sell Signal', marker='v', color='r')
plt.title(f'{ticker} Historical Close Prices with Wavelet Transform Buy and Sell Signals')
plt.legend()
plt.show()

2.7 完整的 Python 代码

       将所有代码组合在一起,我们可以得到:

import numpy as np
import pandas as pd
import matplotlib.pyplot as plt
import yfinance as yf
from scipy import signal

# Download ASML.AS historical stock data
ticker = "SIE.DE"
stock_data = yf.download(ticker, start="2018-01-01", end="2023-12-30")

# Compute the Continuous Wavelet Transform (CWT) using the Ricker wavelet
widths = np.arange(1, 15)
cwt_result = signal.cwt(stock_data['Close'].values, signal.ricker, widths)

# Extract the relevant CWT coefficients for analysis
cwt_positive = np.where(cwt_result > 0, cwt_result, 0)
cwt_negative = np.where(cwt_result < 0, cwt_result, 0)

# Calculate the buy and sell signals from the CWT coefficients
buy_signal = pd.Series(np.sum(cwt_positive, axis=0), index=stock_data.index)
sell_signal = pd.Series(-np.sum(cwt_negative, axis=0), index=stock_data.index)

# Identify buy and sell signal crossovers
cross_above = (buy_signal >= sell_signal) & (buy_signal.shift(1) < sell_signal.shift(1))
cross_below = (buy_signal <= sell_signal) & (buy_signal.shift(1) > sell_signal.shift(1))

# Plot the stock prices and buy/sell signals
plt.figure(figsize=(30, 6))
plt.plot(stock_data.index, stock_data['Close'], label='Close Prices', alpha=0.5)
plt.scatter(stock_data.index[cross_above], stock_data['Close'][cross_above], label='Buy Signal', marker='^', color='g')
plt.scatter(stock_data.index[cross_below], stock_data['Close'][cross_below], label='Sell Signal', marker='v', color='r')
plt.title(f'{ticker} Historical Close Prices with Wavelet Transform Buy and Sell Signals')
plt.legend()
plt.show()

       如图,2018 年至 2023 年历史收盘价的可视化,以及使用基本 CWT 方法通过小波变换得出的买入(绿色向上三角形)和卖出(红色向下三角形)信号。


2.8 优化参数

       虽然小波变换为分析股票数据和生成买卖信号提供了一种创新方法,但其有效性在很大程度上取决于参数的选择:小波的宽度和阈值。这些参数会影响我们策略的灵敏度和特异性。

       利用下面的代码,你可以实现优化技术,系统地选择最佳参数。利用回溯测试功能,我们将模拟不同参数组合下策略的过往表现。最后,我们将对策略进行微调,以最大限度地提高历史业绩,同时牢记一句格言:过去的业绩并不一定代表未来的结果。

import numpy as np
import pandas as pd
import matplotlib.pyplot as plt
import yfinance as yf
from scipy import signal

# Download ASML.AS historical stock data
ticker = "ASML.AS"
stock_data = yf.download(ticker, start="2018-01-01", end="2023-12-30")

# Define parameter ranges
widths_range = np.arange(1, 15)
threshold_range = np.arange(0.1, 1.0, 0.1)

# Store the best parameters and their performance
best_widths = None
best_threshold = None
best_performance = -np.inf

def backtest(stock_data, cross_above, cross_below):
    # We'll start with one "unit" of cash
    cash = 1.0  
    stock = 0.0  
    position = "out"  

    # Go through each day in our data
    for i in range(len(stock_data)):
        # If we have a buy signal and we're not already holding the stock
        if cross_above[i] and position == "out":
            # Buy the stock
            stock += cash / stock_data['Close'][i]
            cash = 0.0
            position = "in"
        # If we have a sell signal and we're holding the stock
        elif cross_below[i] and position == "in":
            # Sell the stock
            cash += stock * stock_data['Close'][i]
            stock = 0.0
            position = "out"

    # Return our final portfolio value
    if position == "in":
        return cash + stock * stock_data['Close'][-1]
    else:
        return cash

# Go through each combination of parameters
for widths in widths_range:
    for threshold in threshold_range:
        # Compute the CWT
        cwt_result = signal.cwt(stock_data['Close'].values, signal.ricker, [widths])

        # Extract relevant coefficients
        cwt_positive = np.where(cwt_result > threshold, cwt_result, 0)
        cwt_negative = np.where(cwt_result < -threshold, cwt_result, 0)

        # Calculate signals
        buy_signal = pd.Series(np.sum(cwt_positive, axis=0), index=stock_data.index)
        sell_signal = pd.Series(-np.sum(cwt_negative, axis=0), index=stock_data.index)
        cross_above = (buy_signal >= sell_signal) & (buy_signal.shift(1) < sell_signal.shift(1))
        cross_below = (buy_signal <= sell_signal) & (buy_signal.shift(1) > sell_signal.shift(1))

        # Calculate performance based on trading signals
        performance = backtest(stock_data, cross_above, cross_below)

        # Update best parameters if this performance is better
        if performance > best_performance:
            best_performance = performance
            best_widths = widths
            best_threshold = threshold

# Print the best parameters
print(f"Best widths: {best_widths}")
print(f"Best threshold: {best_threshold}")
print(f"Best performance: {best_performance * 100}%")

# Recalculate the CWT and buy/sell signals using the best parameters
cwt_result = signal.cwt(stock_data['Close'].values, signal.ricker, [best_widths])
cwt_positive = np.where(cwt_result > best_threshold, cwt_result, 0)
cwt_negative = np.where(cwt_result < -best_threshold, cwt_result, 0)
buy_signal = pd.Series(np.sum(cwt_positive, axis=0), index=stock_data.index)
sell_signal = pd.Series(-np.sum(cwt_negative, axis=0), index=stock_data.index)
cross_above = (buy_signal >= sell_signal) & (buy_signal.shift(1) < sell_signal.shift(1))
cross_below = (buy_signal <= sell_signal) & (buy_signal.shift(1) > sell_signal.shift(1))

# Plot the stock prices and buy/sell signals
plt.figure(figsize=(30, 6))
plt.plot(stock_data.index, stock_data['Close'], label='Close Prices', alpha=0.5)
plt.scatter(stock_data.index[cross_above], stock_data['Close'][cross_above], label='Buy Signal', marker='^', color='g')
plt.scatter(stock_data.index[cross_below], stock_data['Close'][cross_below], label='Sell Signal', marker='v', color='r')
plt.title(f'{ticker} Historical Close Prices with Wavelet Transform Buy and Sell Signals')
plt.legend()
plt.show()

       如图显示,我们增强了 ASML.AS 2018 年至 2023 年历史收盘价的可视化,突出显示了为提高交易性能而优化小波变换参数后获得的买入和卖出信号。


三、解释和分析

       在对股票数据进行小波变换并使用优化参数对其进行改进后,我们得到了一系列买入和卖出信号。如下:

  1. 对市场噪音的敏感性:与其他技术指标一样,小波变换也会受到市场噪音的影响。不过,小波变换的多尺度特性提供了一定程度的弹性,使其能够突出真正的模式,同时有可能过滤掉短期波动。
  2. 周期性模式:股票经常表现出周期性行为--季度财务报告、年度市场趋势等。小波能够捕捉不同尺度的此类行为,在预测未来走势方面具有显著优势。
  3. 性能指标:虽然我们的优化提高了历史业绩,但在样本外数据上验证该策略至关重要。历史数据上的表现并不能保证未来也会出现类似的结果。意识到潜在的过度拟合至关重要。

四、可能的改进和扩展

  1. 替代小波:我们使用的是 Ricker 小波,但其他一些小波系列,如 Daubechies 或 Haar,可能会提供不同的见解。探索这些小波可能是一个令人兴奋的途径。
  2. 结合其他指标:虽然小波变换提供了一个独特的视角,但将其与其他技术指标相结合可能会加强决策过程。
  3. 机器学习集成:利用机器学习算法有助于更准确地预测买卖信号。小波系数可作为决策树、神经网络等模型的特征。
  4. 自适应阈值法:与静态阈值相比,适应不断变化的市场条件的动态阈值方法可能会提高信号的可靠性。

五、观点回顾

       小波变换为分析股市数据提供了一个独特的视角,兼顾了时间和频率的洞察力。虽然它提供了一个全新的视角,但该技术并非万无一失--选择正确的参数至关重要。这种方法只是了解市场动向的众多工具之一。交易者应将小波分析与其他方法相结合,以获得更敏锐的洞察力。我们的小波分析之旅强调了金融研究的不断创新,始终追求更清晰的理解和预测能力。

  • 小波变换在处理金融时间序列数据时优于傅立叶变换,因为它能够同时捕捉时间和频率信息,适应金融数据的非稳态特性。
  • 连续小波变换(CWT)是一种有效的工具,用于从股票价格数据中提取潜在的买入和卖出信号。
  • 参数选择对小波变换的效果至关重要,需要通过回溯测试来优化小波的宽度和阈值,以提高交易策略的历史性能。
  • 小波变换可以与其他技术分析方法相结合,例如其他技术指标或机器学习算法,以增强预测的准确性和可靠性。
  • 小波分析在金融研究中是一个不断发展的领域,有助于更好地理解市场动态,并提供新的预测工具。

       感谢您阅读到最后,希望本文能给您带来新的收获,非常感谢。如果对文中的内容有任何疑问,请给我留言,必复。


文内容仅仅是技术探讨和学习,并不构成任何投资建议。

转发请注明原作者和出处。


原文地址:https://blog.csdn.net/weixin_70955880/article/details/142964666

免责声明:本站文章内容转载自网络资源,如本站内容侵犯了原著者的合法权益,可联系本站删除。更多内容请关注自学内容网(zxcms.com)!