安装所需的库¶
在运行以下代码之前,请确保已安装所有必要的库。
In [60]:
# 安装所需的库
# %pip install numpy pandas matplotlib yfinance scikit-learn tensorflow
# %conda install ta-lib
示例3:基于CNN的金融时间序列分析与交易信号生成¶
问题描述¶
金融市场中存在各种重要的时间序列模式,能够识别这些模式有助于预测未来市场走势。本示例将使用卷积神经网络(CNN)识别金融时间序列中的重要模式,并构建交易信号生成系统。CNN作为深度学习的重要分支,能够自动提取时间序列数据中的局部特征和全局模式,而无需手动特征工程,这使其成为金融预测的强大工具。
卷积神经网络(CNN)简介¶
CNN通常用于图像处理,但也可以应用于时间序列数据。CNN通过滑动窗口(卷积核)提取局部特征,可以有效识别时间序列中的局部模式。相比传统方法,CNN可以自动学习真实的非线性模式,而不需要手动特征工程。CNN的多层结构能够逐层提取从简单到复杂的特征,特别适合捕捉金融市场中的多尺度模式和时间依赖性。
模型架构¶
- 输入层:接收多个技术指标组成的时间序列矩阵
- 卷积层:提取局部时间模式特征
- 池化层:降维并保留重要特征
- 全连接层:综合所有特征进行分类
- 输出层:预测市场走势(上涨、下跌或横盘)
实现步骤¶
- 获取股票历史数据:收集价格、交易量等原始数据
- 计算多种技术指标作为特征:包括移动平均、RSI、MACD等
- 创建分类标签:标记历史数据中的上涨、下跌或横盘状态
- 构建和训练CNN模型:设计适合时间序列的CNN架构,包括1D卷积层、池化层和全连接层
- 生成交易信号并评估策略表现:根据CNN模型预测结果产生买入、卖出或持有信号
核心思想与优势¶
- 自动特征提取:CNN能够自动从原始时间序列中提取有用的模式,无需人工设计特征
- **捕捉多尺度特征v:通过不同大小的卷积核,可以同时捕捉短期、中期和长期市场模式
- 处理非线性关系:CNN的非线性激活函数能够模拟市场中复杂的非线性关系
- 抗噪能力强:卷积和池化操作能有效过滤市场噪声,提取真正有价值的信号
- 端到端学习:从原始数据直接学习到交易决策,减少人为偏见
通过本示例,学习者将掌握如何应用深度学习技术中的CNN模型分析金融时间序列数据,识别市场中隐藏的模式与规律,并将神经网络的预测结果转化为实际可操作的交易策略,这代表了量化交易领域的前沿技术应用。
In [61]:
import numpy as np
import pandas as pd
import matplotlib.pyplot as plt
import tensorflow as tf
from tensorflow.keras.models import Sequential
from tensorflow.keras.layers import Conv1D, MaxPooling1D, Flatten, Dense, Dropout
from tensorflow.keras.optimizers import Adam
from sklearn.preprocessing import MinMaxScaler
from sklearn.model_selection import train_test_split
import seaborn as sns
from datetime import datetime, timedelta
import random
# 设置随机种子,确保结果可复现
np.random.seed(42)
tf.random.set_seed(42)
random.seed(42)
In [62]:
# 1. 生成模拟金融时间序列数据
def generate_financial_data(n_samples=1000, n_features=5, n_patterns=3):
"""
生成模拟金融时间序列数据,包含若干模式
参数:
- n_samples: 样本数量
- n_features: 特征数量(如开盘价、收盘价、成交量等)
- n_patterns: 要插入的模式数量
返回:
- 金融时间序列数据
- 模式出现的位置(用于标记)
"""
# 基础时间序列 - 使用随机游走模拟价格
base_price = 100.0
volatility = 0.01
# 使用随机游走生成价格序列
price_changes = np.random.normal(0, volatility, n_samples)
price_series = base_price * np.exp(np.cumsum(price_changes))
# 创建DataFrame
dates = [datetime.now() + timedelta(days=i) for i in range(n_samples)]
df = pd.DataFrame({
'date': dates,
'close': price_series,
'high': price_series * np.random.uniform(1.0, 1.03, n_samples),
'open': price_series * np.random.uniform(0.99, 1.01, n_samples),
'low': price_series * np.random.uniform(0.97, 1.0, n_samples),
'volume': np.random.uniform(1000000, 5000000, n_samples)
})
# 计算一些技术指标
df['ma5'] = df['close'].rolling(5).mean()
df['ma20'] = df['close'].rolling(20).mean()
df['rsi'] = calculate_rsi(df['close'], 14)
df['volatility'] = df['close'].rolling(10).std()
df['momentum'] = df['close'] - df['close'].shift(5)
# 丢弃包含NaN的行
df = df.dropna()
# 插入模式(如头肩顶、双底等)和对应的交易信号
pattern_locations = []
# 模式1:上涨趋势突破
for i in range(n_patterns):
start_idx = random.randint(50, len(df) - 30)
length = random.randint(15, 25)
# 创建上涨趋势突破模式
trend_strength = random.uniform(0.01, 0.03)
for j in range(length):
df.loc[start_idx + j, 'close'] *= (1 + trend_strength * j/length)
df.loc[start_idx + j, 'high'] = df.loc[start_idx + j, 'close'] * random.uniform(1.0, 1.02)
df.loc[start_idx + j, 'low'] = df.loc[start_idx + j, 'close'] * random.uniform(0.98, 1.0)
# 记录模式位置和类型
pattern_locations.append((start_idx, start_idx + length, 'uptrend_breakout'))
# 模式2:下跌趋势反转
for i in range(n_patterns):
start_idx = random.randint(50, len(df) - 30)
length = random.randint(15, 25)
# 创建下跌然后反转的模式
for j in range(length):
if j < length // 2:
df.loc[start_idx + j, 'close'] *= (1 - 0.01)
else:
df.loc[start_idx + j, 'close'] *= (1 + 0.015)
df.loc[start_idx + j, 'high'] = df.loc[start_idx + j, 'close'] * random.uniform(1.0, 1.02)
df.loc[start_idx + j, 'low'] = df.loc[start_idx + j, 'close'] * random.uniform(0.98, 1.0)
# 记录模式位置和类型
pattern_locations.append((start_idx, start_idx + length, 'downtrend_reversal'))
# 模式3:盘整突破
for i in range(n_patterns):
start_idx = random.randint(50, len(df) - 30)
length = random.randint(20, 30)
# 创建盘整区间
base_val = df.loc[start_idx, 'close']
for j in range(length):
if j < length - 5:
# 盘整期
df.loc[start_idx + j, 'close'] = base_val * random.uniform(0.98, 1.02)
else:
# 突破
df.loc[start_idx + j, 'close'] = base_val * (1 + 0.01 * (j - (length - 5)))
df.loc[start_idx + j, 'high'] = df.loc[start_idx + j, 'close'] * random.uniform(1.0, 1.02)
df.loc[start_idx + j, 'low'] = df.loc[start_idx + j, 'close'] * random.uniform(0.98, 1.0)
# 记录模式位置和类型
pattern_locations.append((start_idx, start_idx + length, 'consolidation_breakout'))
# 重新计算技术指标
df['ma5'] = df['close'].rolling(5).mean()
df['ma20'] = df['close'].rolling(20).mean()
df['rsi'] = calculate_rsi(df['close'], 14)
df['volatility'] = df['close'].rolling(10).std()
df['momentum'] = df['close'] - df['close'].shift(5)
# 丢弃包含NaN的行
df = df.dropna().reset_index(drop=True)
# 调整模式位置索引以匹配新的dataframe索引
adjusted_locations = []
for start, end, pattern_type in pattern_locations:
if start >= len(df) or end >= len(df):
continue
adjusted_locations.append((start, end, pattern_type))
# 创建信号列
df['signal'] = 0 # 0: 无信号, 1: 买入信号, -1: 卖出信号
# 根据模式设置信号
for start, end, pattern_type in adjusted_locations:
if pattern_type == 'uptrend_breakout':
df.loc[end, 'signal'] = 1 # 上涨趋势突破 - 买入
elif pattern_type == 'downtrend_reversal':
df.loc[end, 'signal'] = 1 # 下跌趋势反转 - 买入
elif pattern_type == 'consolidation_breakout':
df.loc[end, 'signal'] = 1 # 盘整突破后 - 买入
return df, adjusted_locations
# 计算RSI指标
def calculate_rsi(prices, period=14):
"""计算相对强弱指标(RSI)"""
delta = prices.diff()
gain = delta.where(delta > 0, 0)
loss = -delta.where(delta < 0, 0)
avg_gain = gain.rolling(window=period).mean()
avg_loss = loss.rolling(window=period).mean()
rs = avg_gain / avg_loss
rsi = 100 - (100 / (1 + rs))
return rsi
In [63]:
# 2. 创建特征和标签
def prepare_data(df, window_size=20):
"""
准备CNN模型的输入数据和标签
参数:
- df: 包含金融数据的DataFrame
- window_size: 时间窗口大小(CNN的输入序列长度)
返回:
- X: 特征数据 (n_samples, window_size, n_features)
- y: 标签 (n_samples,)(表示窗口之后是否有交易信号)
"""
# 选择特征
features = ['close', 'open', 'high', 'low', 'volume', 'ma5', 'ma20', 'rsi', 'volatility', 'momentum']
data = df[features].values
# 标准化数据
scaler = MinMaxScaler()
data_scaled = scaler.fit_transform(data)
X = []
y = []
# 创建时间窗口和对应的标签
for i in range(len(df) - window_size):
X.append(data_scaled[i:i+window_size])
# 窗口之后的5个时间点内是否有交易信号
future_window = df['signal'].iloc[i+window_size:i+window_size+5].values
if 1 in future_window:
y.append(1) # 买入信号
elif -1 in future_window:
y.append(-1) # 卖出信号
else:
y.append(0) # 无信号
return np.array(X), np.array(y)
In [64]:
# 3. 构建CNN模型
def build_cnn_model(input_shape, num_classes):
"""
构建用于识别金融时间序列模式的CNN模型
参数:
- input_shape: 输入数据形状 (window_size, n_features)
- num_classes: 输出类别数量
返回:
- 编译好的CNN模型
"""
model = Sequential([
# 第一个卷积层
Conv1D(filters=64, kernel_size=3, activation='relu', padding='same', input_shape=input_shape),
MaxPooling1D(pool_size=2),
# 第二个卷积层
Conv1D(filters=128, kernel_size=3, activation='relu', padding='same'),
MaxPooling1D(pool_size=2),
# 第三个卷积层
Conv1D(filters=256, kernel_size=3, activation='relu', padding='same'),
MaxPooling1D(pool_size=2),
# 展平层
Flatten(),
# 全连接层
Dense(128, activation='relu'),
Dropout(0.5),
Dense(64, activation='relu'),
Dropout(0.3),
# 输出层
Dense(num_classes, activation='softmax')
])
# 编译模型
model.compile(
optimizer=Adam(learning_rate=0.001),
loss='sparse_categorical_crossentropy',
metrics=['accuracy']
)
return model
In [65]:
# 4. 模型训练和评估
def train_and_evaluate_model(X, y, test_size=0.2):
"""
训练和评估CNN模型
参数:
- X: 特征数据
- y: 标签
- test_size: 测试集比例
返回:
- 训练好的模型
- 测试集数据
"""
# 划分训练集和测试集
X_train, X_test, y_train, y_test = train_test_split(X, y, test_size=test_size, random_state=42)
# 将标签转换为类别索引(0, 1, 2分别代表无信号、买入信号、卖出信号)
y_train = np.where(y_train == -1, 2, y_train)
y_test = np.where(y_test == -1, 2, y_test)
# 构建模型
model = build_cnn_model(input_shape=(X.shape[1], X.shape[2]), num_classes=3)
# 模型摘要
model.summary()
# 训练模型
history = model.fit(
X_train, y_train,
epochs=50,
batch_size=32,
validation_split=0.2,
verbose=1
)
# 评估模型
loss, accuracy = model.evaluate(X_test, y_test)
print(f"测试集损失: {loss:.4f}")
print(f"测试集准确率: {accuracy:.4f}")
# 绘制训练过程
plt.figure(figsize=(12, 5))
plt.subplot(1, 2, 1)
plt.plot(history.history['loss'], label='训练损失')
plt.plot(history.history['val_loss'], label='验证损失')
plt.title('模型损失')
plt.xlabel('迭代次数')
plt.ylabel('损失')
plt.legend()
plt.subplot(1, 2, 2)
plt.plot(history.history['accuracy'], label='训练准确率')
plt.plot(history.history['val_accuracy'], label='验证准确率')
plt.title('模型准确率')
plt.xlabel('迭代次数')
plt.ylabel('准确率')
plt.legend()
plt.tight_layout()
plt.show()
return model, X_test, y_test
In [66]:
# 5. 交易信号生成
def generate_trading_signals(model, X_test, test_df, window_size=20):
"""
使用训练好的CNN模型生成交易信号
参数:
- model: 训练好的CNN模型
- X_test: 测试集特征数据
- test_df: 测试集对应的原始DataFrame
- window_size: 时间窗口大小
返回:
- 带有预测信号的DataFrame
"""
# 预测测试集
y_pred_proba = model.predict(X_test)
y_pred = np.argmax(y_pred_proba, axis=1)
# 将类别索引转换回信号值(0:无信号, 1:买入, 2:卖出)
y_pred = np.where(y_pred == 2, -1, y_pred)
# 创建包含预测信号的DataFrame
signals_df = test_df.iloc[window_size:window_size+len(y_pred)].copy()
signals_df['predicted_signal'] = y_pred
# 添加信号概率
signals_df['no_signal_prob'] = y_pred_proba[:, 0]
signals_df['buy_signal_prob'] = y_pred_proba[:, 1]
signals_df['sell_signal_prob'] = y_pred_proba[:, 2]
return signals_df
In [67]:
# 6. 回测和评估交易策略
def backtest_strategy(signals_df, initial_capital=10000):
"""
回测基于CNN模型生成的交易信号的交易策略
参数:
- signals_df: 包含交易信号的DataFrame
- initial_capital: 初始资金
返回:
- 包含交易结果的DataFrame
"""
# 创建回测结果DataFrame
backtest_df = signals_df.copy()
# 初始化
backtest_df['position'] = 0
backtest_df['capital'] = initial_capital
backtest_df['holdings'] = 0
backtest_df['portfolio_value'] = initial_capital
# 将index转换成date
backtest_df.index = pd.date_range(start='2020-01-01', periods=len(backtest_df))
# 当前持仓状态(0:空仓, 1:多头持仓, -1:空头持仓)
position = 0
# 遍历每个交易日
for i in range(1, len(backtest_df)):
prev_row = backtest_df.iloc[i-1]
curr_row = backtest_df.iloc[i]
# 获取前一日状态
prev_capital = prev_row['capital']
prev_holdings = prev_row['holdings']
prev_position = prev_row['position']
# 默认不操作
capital = prev_capital
holdings = prev_holdings
position = prev_position
# 处理交易信号
if prev_row['predicted_signal'] == 1 and prev_position != 1: # 买入信号
# 平掉空头仓位(如果有)
if prev_position == -1:
capital = prev_capital + prev_holdings * curr_row['close']
holdings = 0
# 进入多头
holdings = capital / curr_row['close']
capital = 0
position = 1
elif prev_row['predicted_signal'] == -1 and prev_position != -1: # 卖出信号
# 平掉多头仓位(如果有)
if prev_position == 1:
capital = prev_capital + prev_holdings * curr_row['close']
holdings = 0
# 进入空头(简化处理,不考虑卖空)
position = -1
holdings = 0
# 更新DataFrame
backtest_df.loc[backtest_df.index[i], 'position'] = position
backtest_df.loc[backtest_df.index[i], 'capital'] = capital
backtest_df.loc[backtest_df.index[i], 'holdings'] = holdings
# 计算当前投资组合价值
if position == 1: # 多头
portfolio_value = holdings * curr_row['close']
elif position == -1: # 空头(简化,不考虑卖空收益)
portfolio_value = capital
else: # 空仓
portfolio_value = capital
backtest_df.loc[backtest_df.index[i], 'portfolio_value'] = portfolio_value
# 计算回测指标
backtest_df['returns'] = backtest_df['portfolio_value'].pct_change()
backtest_df['cumulative_returns'] = (1 + backtest_df['returns']).cumprod() - 1
# 计算年化收益率
total_days = (backtest_df.index[-1] - backtest_df.index[0]).days
annual_returns = ((1 + backtest_df['cumulative_returns'].iloc[-1]) ** (365 / total_days if total_days > 0 else 1)) - 1
# 计算夏普比率
risk_free_rate = 0.0 # 假设无风险利率为0
sharpe_ratio = np.sqrt(252) * (backtest_df['returns'].mean() - risk_free_rate) / backtest_df['returns'].std()
# 计算最大回撤
backtest_df['peak'] = backtest_df['portfolio_value'].cummax()
backtest_df['drawdown'] = (backtest_df['portfolio_value'] - backtest_df['peak']) / backtest_df['peak']
max_drawdown = backtest_df['drawdown'].min()
# 输出回测结果
print(f"初始资金: ${initial_capital:.2f}")
print(f"最终价值: ${backtest_df['portfolio_value'].iloc[-1]:.2f}")
print(f"总收益率: {backtest_df['cumulative_returns'].iloc[-1]:.2%}")
print(f"年化收益率: {annual_returns:.2%}")
print(f"夏普比率: {sharpe_ratio:.4f}")
print(f"最大回撤: {max_drawdown:.2%}")
return backtest_df
In [68]:
# 7. 可视化结果
def visualize_results(df, signals_df, backtest_df):
"""
可视化交易信号和回测结果
参数:
- df: 原始数据DataFrame
- signals_df: 包含交易信号的DataFrame
- backtest_df: 回测结果DataFrame
"""
# 设置绘图样式
plt.style.use('seaborn-v0_8-darkgrid')
# 图1: 价格和交易信号
plt.figure(figsize=(16, 12))
plt.subplot(3, 1, 1)
plt.plot(signals_df.index, signals_df['close'], label='收盘价', color='blue')
# 标记买入信号
buy_signals = signals_df[signals_df['predicted_signal'] == 1]
plt.scatter(buy_signals.index, buy_signals['close'], marker='^', color='green', s=100, label='买入信号')
# 标记卖出信号
sell_signals = signals_df[signals_df['predicted_signal'] == -1]
plt.scatter(sell_signals.index, sell_signals['close'], marker='v', color='red', s=100, label='卖出信号')
plt.title('价格和CNN生成的交易信号')
plt.ylabel('价格')
plt.legend()
# 图2: 投资组合价值
plt.subplot(3, 1, 2)
plt.plot(backtest_df.index, backtest_df['portfolio_value'], label='投资组合价值', color='purple')
plt.title('投资组合价值变化')
plt.ylabel('投资组合价值')
plt.legend()
# 图3: 累计收益率
plt.subplot(3, 1, 3)
plt.plot(backtest_df.index, backtest_df['cumulative_returns'] * 100, label='策略累计收益率%', color='orange')
plt.axhline(y=0, color='black', linestyle='--')
plt.title('累计收益率')
plt.ylabel('累计收益率 (%)')
plt.legend()
plt.tight_layout()
plt.show()
# 图4: 信号概率热图
plt.figure(figsize=(16, 6))
# 创建信号概率的热图数据
proba_data = signals_df[['no_signal_prob', 'buy_signal_prob', 'sell_signal_prob']].iloc[-50:].T
proba_data.columns = [f'Day {i+1}' for i in range(proba_data.shape[1])]
proba_data.index = ['无信号', '买入信号', '卖出信号']
# 绘制热图
sns.heatmap(proba_data, cmap='YlGnBu', annot=True, fmt='.2f', cbar_kws={'label': '概率'})
plt.title('最近50天交易信号概率热图')
plt.tight_layout()
plt.show()
# 图5: 模式识别结果展示
plt.figure(figsize=(16, 8))
# 找出典型的买入和卖出信号样本
buy_samples = signals_df[signals_df['predicted_signal'] == 1].sample(min(3, len(buy_signals)))
for i, (idx, row) in enumerate(buy_samples.iterrows()):
sample_window_idx = idx - 20 # 假设窗口大小为20
if sample_window_idx >= 0:
sample_data = df.loc[sample_window_idx:idx, 'close']
plt.subplot(2, 3, i+1)
plt.plot(range(len(sample_data)), sample_data.values)
plt.axvline(x=len(sample_data)-1, color='green', linestyle='--')
plt.title(f'买入信号模式 {i+1}')
plt.xticks([])
sell_samples = signals_df[signals_df['predicted_signal'] == -1].sample(min(3, len(sell_signals)))
for i, (idx, row) in enumerate(sell_samples.iterrows()):
sample_window_idx = idx - 20 # 假设窗口大小为20
if sample_window_idx >= 0:
sample_data = df.loc[sample_window_idx:idx, 'close']
plt.subplot(2, 3, i+4)
plt.plot(range(len(sample_data)), sample_data.values)
plt.axvline(x=len(sample_data)-1, color='red', linestyle='--')
plt.title(f'卖出信号模式 {i+1}')
plt.xticks([])
plt.tight_layout()
plt.show()
# 主函数
def main():
"""主函数,运行整个CNN金融时间序列模式识别和交易信号生成系统"""
# 1. 生成模拟金融时间序列数据
print("正在生成模拟金融时间序列数据...")
df, pattern_locations = generate_financial_data(n_samples=1500, n_features=5, n_patterns=15)
print(f"数据生成完成,总样本数: {len(df)}")
print(f"插入的模式总数: {len(pattern_locations)}")
print("数据预览:")
print(df.head())
# 展示数据和信号
plt.figure(figsize=(14, 7))
plt.plot(df['close'], label='收盘价')
# 标记模式
for start, end, pattern_type in pattern_locations:
plt.axvspan(start, end, alpha=0.2, color='yellow')
if pattern_type == 'uptrend_breakout':
plt.scatter(end, df.loc[end, 'close'], marker='^', color='green', s=100)
elif pattern_type == 'downtrend_reversal':
plt.scatter(end, df.loc[end, 'close'], marker='^', color='blue', s=100)
elif pattern_type == 'consolidation_breakout':
plt.scatter(end, df.loc[end, 'close'], marker='^', color='purple', s=100)
plt.title('模拟金融时间序列数据及模式')
plt.legend()
plt.show()
# 2. 准备特征和标签
print("正在准备CNN模型的输入数据和标签...")
window_size = 20
X, y = prepare_data(df, window_size=window_size)
print(f"特征形状: {X.shape}")
print(f"标签形状: {y.shape}")
print(f"买入信号比例: {np.sum(y == 1) / len(y):.2%}")
print(f"卖出信号比例: {np.sum(y == -1) / len(y):.2%}")
print(f"无信号比例: {np.sum(y == 0) / len(y):.2%}")
# 3 & 4. 构建、训练和评估CNN模型
print("正在构建和训练CNN模型...")
model, X_test, y_test = train_and_evaluate_model(X, y)
# 5. 生成交易信号
print("正在生成交易信号...")
test_df = df.iloc[-len(X_test)-window_size:]
signals_df = generate_trading_signals(model, X_test, test_df, window_size)
print("交易信号生成完成!")
# 6. 回测交易策略
print("正在回测交易策略...")
backtest_df = backtest_strategy(signals_df)
# 7. 可视化结果
print("正在可视化结果...")
visualize_results(df, signals_df, backtest_df)
# 保存模型
model.save('cnn_financial_pattern_model.h5')
print("模型已保存为 'cnn_financial_pattern_model.h5'")
# 运行主程序
if __name__ == "__main__":
main()
正在生成模拟金融时间序列数据... 数据生成完成,总样本数: 1462 插入的模式总数: 45 数据预览: date close high open low \ 0 2025-06-20 12:17:42.114553 91.445836 92.265352 92.064293 89.394265 1 2025-06-21 12:17:42.114554 91.626034 94.211756 92.180942 90.105207 2 2025-06-22 12:17:42.114554 92.305167 93.023200 93.156073 89.877863 3 2025-06-23 12:17:42.114555 92.463484 93.655311 92.529938 90.260948 4 2025-06-24 12:17:42.114556 92.356613 94.774686 92.335363 89.588529 volume ma5 ma20 rsi volatility momentum signal 0 3.855832e+06 93.677124 95.355461 29.225408 1.350313 -3.235771 0 1 4.698634e+06 92.909607 95.105160 29.698400 1.585794 -3.837582 0 2 3.068528e+06 92.509592 94.817480 37.282986 1.674726 -2.000074 0 3 1.468258e+06 92.101806 94.548773 36.081499 1.585234 -2.038932 0 4 3.112711e+06 92.039427 94.271418 37.766807 1.419119 -0.311895 0
正在准备CNN模型的输入数据和标签... 特征形状: (1442, 20, 10) 标签形状: (1442,) 买入信号比例: 15.05% 卖出信号比例: 0.00% 无信号比例: 84.95% 正在构建和训练CNN模型...
Model: "sequential_4"
┏━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━┳━━━━━━━━━━━━━━━━━━━━━━━━┳━━━━━━━━━━━━━━━┓ ┃ Layer (type) ┃ Output Shape ┃ Param # ┃ ┡━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━╇━━━━━━━━━━━━━━━━━━━━━━━━╇━━━━━━━━━━━━━━━┩ │ conv1d_8 (Conv1D) │ (None, 20, 64) │ 1,984 │ ├─────────────────────────────────┼────────────────────────┼───────────────┤ │ max_pooling1d_8 (MaxPooling1D) │ (None, 10, 64) │ 0 │ ├─────────────────────────────────┼────────────────────────┼───────────────┤ │ conv1d_9 (Conv1D) │ (None, 10, 128) │ 24,704 │ ├─────────────────────────────────┼────────────────────────┼───────────────┤ │ max_pooling1d_9 (MaxPooling1D) │ (None, 5, 128) │ 0 │ ├─────────────────────────────────┼────────────────────────┼───────────────┤ │ conv1d_10 (Conv1D) │ (None, 5, 256) │ 98,560 │ ├─────────────────────────────────┼────────────────────────┼───────────────┤ │ max_pooling1d_10 (MaxPooling1D) │ (None, 2, 256) │ 0 │ ├─────────────────────────────────┼────────────────────────┼───────────────┤ │ flatten_4 (Flatten) │ (None, 512) │ 0 │ ├─────────────────────────────────┼────────────────────────┼───────────────┤ │ dense_8 (Dense) │ (None, 128) │ 65,664 │ ├─────────────────────────────────┼────────────────────────┼───────────────┤ │ dropout_4 (Dropout) │ (None, 128) │ 0 │ ├─────────────────────────────────┼────────────────────────┼───────────────┤ │ dense_9 (Dense) │ (None, 64) │ 8,256 │ ├─────────────────────────────────┼────────────────────────┼───────────────┤ │ dropout_5 (Dropout) │ (None, 64) │ 0 │ ├─────────────────────────────────┼────────────────────────┼───────────────┤ │ dense_10 (Dense) │ (None, 3) │ 195 │ └─────────────────────────────────┴────────────────────────┴───────────────┘
Total params: 199,363 (778.76 KB)
Trainable params: 199,363 (778.76 KB)
Non-trainable params: 0 (0.00 B)
Epoch 1/50 29/29 ━━━━━━━━━━━━━━━━━━━━ 1s 8ms/step - accuracy: 0.7336 - loss: 0.7506 - val_accuracy: 0.8095 - val_loss: 0.5103 Epoch 2/50 29/29 ━━━━━━━━━━━━━━━━━━━━ 0s 5ms/step - accuracy: 0.8457 - loss: 0.4839 - val_accuracy: 0.8095 - val_loss: 0.5126 Epoch 3/50 29/29 ━━━━━━━━━━━━━━━━━━━━ 0s 5ms/step - accuracy: 0.8561 - loss: 0.4407 - val_accuracy: 0.8095 - val_loss: 0.4991 Epoch 4/50 29/29 ━━━━━━━━━━━━━━━━━━━━ 0s 6ms/step - accuracy: 0.8596 - loss: 0.4277 - val_accuracy: 0.8095 - val_loss: 0.4931 Epoch 5/50 29/29 ━━━━━━━━━━━━━━━━━━━━ 0s 5ms/step - accuracy: 0.8585 - loss: 0.4256 - val_accuracy: 0.8095 - val_loss: 0.4831 Epoch 6/50 29/29 ━━━━━━━━━━━━━━━━━━━━ 0s 5ms/step - accuracy: 0.8585 - loss: 0.4164 - val_accuracy: 0.8095 - val_loss: 0.4728 Epoch 7/50 29/29 ━━━━━━━━━━━━━━━━━━━━ 0s 5ms/step - accuracy: 0.8585 - loss: 0.4095 - val_accuracy: 0.8095 - val_loss: 0.4659 Epoch 8/50 29/29 ━━━━━━━━━━━━━━━━━━━━ 0s 5ms/step - accuracy: 0.8585 - loss: 0.4124 - val_accuracy: 0.8095 - val_loss: 0.4567 Epoch 9/50 29/29 ━━━━━━━━━━━━━━━━━━━━ 0s 5ms/step - accuracy: 0.8585 - loss: 0.3970 - val_accuracy: 0.8095 - val_loss: 0.4463 Epoch 10/50 29/29 ━━━━━━━━━━━━━━━━━━━━ 0s 4ms/step - accuracy: 0.8585 - loss: 0.3832 - val_accuracy: 0.8095 - val_loss: 0.4342 Epoch 11/50 29/29 ━━━━━━━━━━━━━━━━━━━━ 0s 4ms/step - accuracy: 0.8585 - loss: 0.3728 - val_accuracy: 0.8095 - val_loss: 0.4372 Epoch 12/50 29/29 ━━━━━━━━━━━━━━━━━━━━ 0s 4ms/step - accuracy: 0.8585 - loss: 0.3659 - val_accuracy: 0.8095 - val_loss: 0.4243 Epoch 13/50 29/29 ━━━━━━━━━━━━━━━━━━━━ 0s 5ms/step - accuracy: 0.8585 - loss: 0.3506 - val_accuracy: 0.8095 - val_loss: 0.4195 Epoch 14/50 29/29 ━━━━━━━━━━━━━━━━━━━━ 0s 5ms/step - accuracy: 0.8603 - loss: 0.3267 - val_accuracy: 0.8139 - val_loss: 0.3859 Epoch 15/50 29/29 ━━━━━━━━━━━━━━━━━━━━ 0s 5ms/step - accuracy: 0.8719 - loss: 0.2768 - val_accuracy: 0.8312 - val_loss: 0.3747 Epoch 16/50 29/29 ━━━━━━━━━━━━━━━━━━━━ 0s 5ms/step - accuracy: 0.8861 - loss: 0.2686 - val_accuracy: 0.8571 - val_loss: 0.3327 Epoch 17/50 29/29 ━━━━━━━━━━━━━━━━━━━━ 0s 5ms/step - accuracy: 0.9050 - loss: 0.2257 - val_accuracy: 0.8312 - val_loss: 0.4418 Epoch 18/50 29/29 ━━━━━━━━━━━━━━━━━━━━ 0s 5ms/step - accuracy: 0.9334 - loss: 0.1589 - val_accuracy: 0.8398 - val_loss: 0.5175 Epoch 19/50 29/29 ━━━━━━━━━━━━━━━━━━━━ 0s 5ms/step - accuracy: 0.9492 - loss: 0.1266 - val_accuracy: 0.8528 - val_loss: 0.4567 Epoch 20/50 29/29 ━━━━━━━━━━━━━━━━━━━━ 0s 4ms/step - accuracy: 0.9536 - loss: 0.1127 - val_accuracy: 0.8658 - val_loss: 0.3504 Epoch 21/50 29/29 ━━━━━━━━━━━━━━━━━━━━ 0s 6ms/step - accuracy: 0.9800 - loss: 0.0815 - val_accuracy: 0.8355 - val_loss: 0.4320 Epoch 22/50 29/29 ━━━━━━━━━━━━━━━━━━━━ 0s 5ms/step - accuracy: 0.9640 - loss: 0.1195 - val_accuracy: 0.8528 - val_loss: 0.4831 Epoch 23/50 29/29 ━━━━━━━━━━━━━━━━━━━━ 0s 4ms/step - accuracy: 0.9748 - loss: 0.0740 - val_accuracy: 0.8571 - val_loss: 0.6113 Epoch 24/50 29/29 ━━━━━━━━━━━━━━━━━━━━ 0s 5ms/step - accuracy: 0.9572 - loss: 0.1190 - val_accuracy: 0.8485 - val_loss: 0.5522 Epoch 25/50 29/29 ━━━━━━━━━━━━━━━━━━━━ 0s 5ms/step - accuracy: 0.9717 - loss: 0.0745 - val_accuracy: 0.8658 - val_loss: 0.4596 Epoch 26/50 29/29 ━━━━━━━━━━━━━━━━━━━━ 0s 4ms/step - accuracy: 0.9749 - loss: 0.0711 - val_accuracy: 0.8615 - val_loss: 0.4518 Epoch 27/50 29/29 ━━━━━━━━━━━━━━━━━━━━ 0s 4ms/step - accuracy: 0.9609 - loss: 0.0987 - val_accuracy: 0.8528 - val_loss: 0.3498 Epoch 28/50 29/29 ━━━━━━━━━━━━━━━━━━━━ 0s 5ms/step - accuracy: 0.9709 - loss: 0.0845 - val_accuracy: 0.8745 - val_loss: 0.4168 Epoch 29/50 29/29 ━━━━━━━━━━━━━━━━━━━━ 0s 5ms/step - accuracy: 0.9996 - loss: 0.0204 - val_accuracy: 0.8745 - val_loss: 0.5037 Epoch 30/50 29/29 ━━━━━━━━━━━━━━━━━━━━ 0s 5ms/step - accuracy: 0.9953 - loss: 0.0116 - val_accuracy: 0.8571 - val_loss: 0.5987 Epoch 31/50 29/29 ━━━━━━━━━━━━━━━━━━━━ 0s 4ms/step - accuracy: 0.9977 - loss: 0.0068 - val_accuracy: 0.8615 - val_loss: 0.6389 Epoch 32/50 29/29 ━━━━━━━━━━━━━━━━━━━━ 0s 5ms/step - accuracy: 0.9900 - loss: 0.0186 - val_accuracy: 0.8615 - val_loss: 0.7089 Epoch 33/50 29/29 ━━━━━━━━━━━━━━━━━━━━ 0s 6ms/step - accuracy: 0.9984 - loss: 0.0086 - val_accuracy: 0.8571 - val_loss: 0.7214 Epoch 34/50 29/29 ━━━━━━━━━━━━━━━━━━━━ 0s 5ms/step - accuracy: 0.9981 - loss: 0.0058 - val_accuracy: 0.8658 - val_loss: 0.7180 Epoch 35/50 29/29 ━━━━━━━━━━━━━━━━━━━━ 0s 5ms/step - accuracy: 0.9999 - loss: 0.0052 - val_accuracy: 0.8874 - val_loss: 0.6385 Epoch 36/50 29/29 ━━━━━━━━━━━━━━━━━━━━ 0s 5ms/step - accuracy: 1.0000 - loss: 0.0041 - val_accuracy: 0.8528 - val_loss: 0.7256 Epoch 37/50 29/29 ━━━━━━━━━━━━━━━━━━━━ 0s 7ms/step - accuracy: 1.0000 - loss: 9.3001e-04 - val_accuracy: 0.8571 - val_loss: 0.7633 Epoch 38/50 29/29 ━━━━━━━━━━━━━━━━━━━━ 0s 4ms/step - accuracy: 1.0000 - loss: 0.0011 - val_accuracy: 0.8571 - val_loss: 0.7785 Epoch 39/50 29/29 ━━━━━━━━━━━━━━━━━━━━ 0s 4ms/step - accuracy: 1.0000 - loss: 8.2964e-04 - val_accuracy: 0.8615 - val_loss: 0.8021 Epoch 40/50 29/29 ━━━━━━━━━━━━━━━━━━━━ 0s 5ms/step - accuracy: 1.0000 - loss: 5.4312e-04 - val_accuracy: 0.8571 - val_loss: 0.8402 Epoch 41/50 29/29 ━━━━━━━━━━━━━━━━━━━━ 0s 4ms/step - accuracy: 1.0000 - loss: 2.0121e-04 - val_accuracy: 0.8658 - val_loss: 0.8161 Epoch 42/50 29/29 ━━━━━━━━━━━━━━━━━━━━ 0s 5ms/step - accuracy: 1.0000 - loss: 2.9432e-04 - val_accuracy: 0.8615 - val_loss: 0.8531 Epoch 43/50 29/29 ━━━━━━━━━━━━━━━━━━━━ 0s 4ms/step - accuracy: 1.0000 - loss: 5.2659e-04 - val_accuracy: 0.8571 - val_loss: 0.8734 Epoch 44/50 29/29 ━━━━━━━━━━━━━━━━━━━━ 0s 5ms/step - accuracy: 1.0000 - loss: 3.1029e-04 - val_accuracy: 0.8615 - val_loss: 0.8830 Epoch 45/50 29/29 ━━━━━━━━━━━━━━━━━━━━ 0s 4ms/step - accuracy: 1.0000 - loss: 6.9775e-04 - val_accuracy: 0.8615 - val_loss: 0.9369 Epoch 46/50 29/29 ━━━━━━━━━━━━━━━━━━━━ 0s 4ms/step - accuracy: 0.9999 - loss: 5.4823e-04 - val_accuracy: 0.8615 - val_loss: 0.8730 Epoch 47/50 29/29 ━━━━━━━━━━━━━━━━━━━━ 0s 5ms/step - accuracy: 1.0000 - loss: 3.4415e-04 - val_accuracy: 0.8528 - val_loss: 0.8551 Epoch 48/50 29/29 ━━━━━━━━━━━━━━━━━━━━ 0s 7ms/step - accuracy: 1.0000 - loss: 1.7816e-04 - val_accuracy: 0.8615 - val_loss: 0.8836 Epoch 49/50 29/29 ━━━━━━━━━━━━━━━━━━━━ 0s 5ms/step - accuracy: 1.0000 - loss: 3.5464e-04 - val_accuracy: 0.8658 - val_loss: 0.8836 Epoch 50/50 29/29 ━━━━━━━━━━━━━━━━━━━━ 0s 5ms/step - accuracy: 1.0000 - loss: 2.0189e-04 - val_accuracy: 0.8615 - val_loss: 0.9054 10/10 ━━━━━━━━━━━━━━━━━━━━ 0s 2ms/step - accuracy: 0.8607 - loss: 0.9462 测试集损失: 0.9459 测试集准确率: 0.8616
正在生成交易信号... 10/10 ━━━━━━━━━━━━━━━━━━━━ 0s 7ms/step 交易信号生成完成! 正在回测交易策略... 初始资金: $10000.00 最终价值: $13048.38 总收益率: 30.48% 年化收益率: 40.10% 夏普比率: 1.3257 最大回撤: -13.08% 正在可视化结果...
WARNING:absl:You are saving your model as an HDF5 file via `model.save()` or `keras.saving.save_model(model)`. This file format is considered legacy. We recommend using instead the native Keras format, e.g. `model.save('my_model.keras')` or `keras.saving.save_model(model, 'my_model.keras')`.
模型已保存为 'cnn_financial_pattern_model.h5'