Skip to content

Commit

Permalink
feat: 添加股票价格的拉普拉斯可视化和基于FFT的基本指标计算
Browse files Browse the repository at this point in the history
  • Loading branch information
StevenChen16 committed Dec 16, 2024
1 parent b9525d9 commit ede2e22
Show file tree
Hide file tree
Showing 2 changed files with 115 additions and 19 deletions.
86 changes: 86 additions & 0 deletions Laplace-display.py
Original file line number Diff line number Diff line change
@@ -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}")
48 changes: 29 additions & 19 deletions ftt/ftt.py → fft/fft.py
Original file line number Diff line number Diff line change
Expand Up @@ -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):
"""执行傅里叶变换,可选择性地过滤高频成分"""
Expand Down Expand Up @@ -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=== 股票分析摘要 ===")

# 基本统计
Expand All @@ -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}")

# 市场状态
Expand Down

0 comments on commit ede2e22

Please sign in to comment.