From ede2e22358bdbcace7ae8e07553063ac31665326 Mon Sep 17 00:00:00 2001 From: Steven Chen Date: Mon, 16 Dec 2024 11:42:24 -0500 Subject: [PATCH] =?UTF-8?q?feat:=20=E6=B7=BB=E5=8A=A0=E8=82=A1=E7=A5=A8?= =?UTF-8?q?=E4=BB=B7=E6=A0=BC=E7=9A=84=E6=8B=89=E6=99=AE=E6=8B=89=E6=96=AF?= =?UTF-8?q?=E5=8F=AF=E8=A7=86=E5=8C=96=E5=92=8C=E5=9F=BA=E4=BA=8EFFT?= =?UTF-8?q?=E7=9A=84=E5=9F=BA=E6=9C=AC=E6=8C=87=E6=A0=87=E8=AE=A1=E7=AE=97?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- Laplace-display.py | 86 ++++++++++++++++++++++++++++++++++++++++ ftt/ftt.py => fft/fft.py | 48 +++++++++++++--------- 2 files changed, 115 insertions(+), 19 deletions(-) create mode 100644 Laplace-display.py rename ftt/ftt.py => fft/fft.py (92%) diff --git a/Laplace-display.py b/Laplace-display.py new file mode 100644 index 0000000..6ea436d --- /dev/null +++ b/Laplace-display.py @@ -0,0 +1,86 @@ +import yfinance as yf +import numpy as np +import matplotlib.pyplot as plt +from scipy import ndimage +import pandas as pd + +# Define stock symbols +stocks = ['AAPL', 'MSFT', 'GOOGL', 'META', 'NVDA', 'TSLA', 'AMD', 'INTC'] + +# Download data +# Use one year of daily data +end_date = pd.Timestamp.today() +start_date = end_date - pd.DateOffset(years=1) + +# Create empty DataFrame to store prices +df = pd.DataFrame() + +# Download data for each stock +for symbol in stocks: + stock = yf.download(symbol, start=start_date, end=end_date, progress=False) + df[symbol] = stock['Close'] + +# Fill any missing values using forward fill +df = df.fillna(method='ffill') + +# Normalize prices to start from 100 for better comparison +normalized_prices = df.div(df.iloc[0]) * 100 + +# Convert to numpy array for Laplacian calculation +prices_array = normalized_prices.values.T # Shape: (n_stocks, n_times) + +# Calculate Laplacian +laplacian = ndimage.laplace(prices_array) + +# Create visualization +fig, (ax1, ax2, ax3) = plt.subplots(3, 1, figsize=(15, 15)) + +# Plot normalized prices +for i, symbol in enumerate(stocks): + ax1.plot(normalized_prices.index, normalized_prices[symbol], + label=symbol, alpha=0.7) +ax1.set_title('Normalized Stock Prices (Starting at 100)') +ax1.set_xlabel('Date') +ax1.set_ylabel('Normalized Price') +ax1.legend(bbox_to_anchor=(1.05, 1), loc='upper left') +ax1.grid(True) + +# Plot prices as a heatmap +im2 = ax2.imshow(prices_array, aspect='auto', cmap='viridis', + extent=[0, len(df), 0, len(stocks)]) +ax2.set_title('Price Heatmap') +ax2.set_xlabel('Trading Days') +ax2.set_ylabel('Stock') +ax2.set_yticks(np.arange(len(stocks)) + 0.5) +ax2.set_yticklabels(stocks) +plt.colorbar(im2, ax=ax2, label='Normalized Price') + +# Plot Laplacian +im3 = ax3.imshow(laplacian, aspect='auto', cmap='coolwarm', + extent=[0, len(df), 0, len(stocks)]) +ax3.set_title('Laplacian of Price Surface') +ax3.set_xlabel('Trading Days') +ax3.set_ylabel('Stock') +ax3.set_yticks(np.arange(len(stocks)) + 0.5) +ax3.set_yticklabels(stocks) +plt.colorbar(im3, ax=ax3, label='Laplacian Value') + +plt.tight_layout() +plt.show() + +# Print some basic statistics about the Laplacian values +print("\nLaplacian Statistics:") +print(f"Mean Laplacian value: {np.mean(laplacian):.4f}") +print(f"Standard deviation: {np.std(laplacian):.4f}") +print(f"Min value: {np.min(laplacian):.4f}") +print(f"Max value: {np.max(laplacian):.4f}") + +# Find the most significant anomalies +threshold = 2 * np.std(laplacian) +anomalies = np.where(np.abs(laplacian) > threshold) +print("\nSignificant price anomalies (beyond 2 standard deviations):") +for stock_idx, time_idx in zip(*anomalies): + date = df.index[time_idx] + stock = stocks[stock_idx] + laplacian_value = laplacian[stock_idx, time_idx] + print(f"Stock: {stock}, Date: {date.strftime('%Y-%m-%d')}, Laplacian: {laplacian_value:.4f}") \ No newline at end of file diff --git a/ftt/ftt.py b/fft/fft.py similarity index 92% rename from ftt/ftt.py rename to fft/fft.py index ee32fb3..a349805 100644 --- a/ftt/ftt.py +++ b/fft/fft.py @@ -37,15 +37,6 @@ def load_data(self, data): self.df['log_return'] = np.log(self.df['Close'] / self.df['Close'].shift(1)) self.df = self.df.dropna() - def compute_basic_metrics(self): - """计算基本指标""" - # 计算移动平均 - self.df['MA21'] = self.df['Close'].rolling(window=21).mean() - self.df['MA63'] = self.df['Close'].rolling(window=63).mean() - self.df['MA252'] = self.df['Close'].rolling(window=252).mean() - - # 计算波动率 - self.df['vol_21'] = self.df['log_return'].rolling(window=21).std() * np.sqrt(252) def perform_fft(self, filter_threshold=None): """执行傅里叶变换,可选择性地过滤高频成分""" @@ -208,34 +199,53 @@ def analyze_with_different_filters(self, periods=[5, 21, 63]): plt.legend() plt.grid(True) return plt - + + def compute_basic_metrics(self): + """计算基本指标,使用FFT滤波替代移动平均""" + # 计算21日、63日和252日滤波后的价格序列 + self.df['FFT21'] = self.filter_high_frequency(cutoff_period=21) + self.df['FFT63'] = self.filter_high_frequency(cutoff_period=63) + self.df['FFT252'] = self.filter_high_frequency(cutoff_period=252) + + """计算基本指标""" + # 计算移动平均 + self.df['MA21'] = self.df['Close'].rolling(window=21).mean() + self.df['MA63'] = self.df['Close'].rolling(window=63).mean() + self.df['MA252'] = self.df['Close'].rolling(window=252).mean() + + # 计算波动率 + self.df['vol_21'] = self.df['log_return'].rolling(window=21).std() * np.sqrt(252) + def get_trading_signals(self): - """生成交易信号""" - # 基于多个指标综合生成信号 + """基于FFT滤波生成交易信号""" signals = pd.DataFrame(index=self.df.index) - # 1. 趋势信号(基于移动平均) - signals['trend'] = np.where(self.df['MA21'] > self.df['MA63'], 1, -1) + # 使用FFT滤波后的价格序列判断趋势 + signals['trend'] = np.where(self.df['FFT21'] > self.df['FFT63'], 1, -1) - # 2. 波动率信号 + # 波动率信号 vol_mean = self.df['vol_21'].mean() signals['volatility'] = np.where(self.df['vol_21'] > vol_mean, 'high', 'low') - # 3. 相位信号(基于希尔伯特变换) + # 相位信号 analytic_signal = hilbert(self.df['log_return'].values) phase = np.angle(analytic_signal) phase_diff = np.diff(phase) - phase_diff = np.append(phase_diff, phase_diff[-1]) # 补充最后一个值 + phase_diff = np.append(phase_diff, phase_diff[-1]) signals['phase'] = np.where(phase_diff > 0, 1, -1) return signals def print_analysis_summary(self): """打印分析摘要""" - # 确保先执行FFT分析和寻找显著周期 + # 执行FFT分析和寻找显著周期 self.perform_fft() self.find_significant_periods() + # 计算各周期滤波 + fft21 = self.filter_high_frequency(cutoff_period=21) + fft63 = self.filter_high_frequency(cutoff_period=63) + print("\n=== 股票分析摘要 ===") # 基本统计 @@ -252,7 +262,7 @@ def print_analysis_summary(self): # 趋势分析 print("\n3. 趋势分析:") - current_trend = "上升" if self.df['MA21'].iloc[-1] > self.df['MA63'].iloc[-1] else "下降" + current_trend = "上升" if fft21.iloc[-1] > fft63.iloc[-1] else "下降" print(f"当前趋势: {current_trend}") # 市场状态