基于Transformer的多资产收益预测模型实战(附PyTorch实现与避坑指南)
基于Transformer的多资产收益预测模型实战(附PyTorch模型训练及可视化完整代码)
一、项目背景与目标
在量化投资领域,利用时间序列数据预测资产收益是核心任务之一。传统方法如LSTM难以捕捉资产间的复杂依赖关系,而Transformer架构通过自注意力机制能有效建模多资产间的联动效应。
本文将从零开始构建一个基于PyTorch的多资产收益预测模型,涵盖数据生成、特征工程、模型设计、训练及可视化全流程,适合深度学习与量化投资的初学者入门。
二、核心技术栈
- 数据处理:Pandas/Numpy(数据生成与预处理)
- 深度学习框架:PyTorch(模型构建与训练)
- 可视化:Matplotlib(结果分析)
- 核心算法:Transformer(自注意力机制)
三、数据生成与预处理
1. 模拟金融数据生成
我们通过以下步骤生成包含5只资产的时间序列数据:
- 市场基准因子:模拟市场整体趋势(几何布朗运动)
- 行业因子:引入周期性波动区分不同行业(如科技、消费、能源)
- 特质因子:每只资产的独立噪声
def generate_market_data(days=2000, n_assets=5): np.random.seed(42) market = np.cumprod(1 + np.random.normal(0.0003, 0.015, days)) # 市场基准 assets = [] sector_map = {0: "Tech", 1: "Tech", 2: "Consume", 3: "Consume", 4: "Energy"} for i in range(n_assets): sector_factor = 0.3 * np.sin(i * 0.8 + np.linspace(0, 10 * np.pi, days)) # 行业周期因子 idiosyncratic = np.cumprod(1 + np.random.normal(0.0002, 0.02, days)) # 特质因子 price = market * (1 + sector_factor) * idiosyncratic # 价格合成 assets.append(price) dates = pd.date_range("2015-01-01", periods=days) return pd.DataFrame(np.array(assets).T, index=dates, columns=[f"Asset_{i}" for i in range(n_assets)])
2. 数据形状说明
生成的DataFrame形状为[2000天, 5资产]
,索引为时间戳,列名为Asset_0到Asset_4。
四、特征工程:从价格到可训练数据
1. 基础时间序列特征
为每只资产计算以下特征:
- 收益率(Return):相邻日价格变化率
- 波动率(Volatility):20日滚动标准差年化
- 移动平均(MA10):10日价格移动平均
- 行业相对强弱(Sector_RS):资产价格与所属行业平均价格的比值
def create_features(data, lookback=60): n_assets = data.shape[1] sector_map = {0: "Tech", 1: "Tech", 2: "Consume", 3: "Consume", 4: "Energy"} features = [] for i, asset in enumerate(data.columns): df = pd.DataFrame() df["Return"] = data[asset].pct_change() df["Volatility"] = df["Return"].rolling(20).std() * np.sqrt(252) # 年化波动率 df["MA10"] = data[asset].rolling(10).mean() # 计算行业相对强弱 sector = sector_map[i] sector_cols = [col for col in data.columns if sector_map[int(col.split("_")[1])] == sector] df["Sector_RS"] = data[asset] / data[sector_cols].mean(axis=1) features.append(df.dropna()) # 去除NaN # 对齐时间索引 common_idx = features[0].index for df in features[1:]: common_idx = common_idx.intersection(df.index) features = [df.loc[common_idx] for df in features] # 构建3D特征张量 [样本数, 时间步, 资产数, 特征数] X = np.stack([np.stack([feat.iloc[i-lookback:i] for i in range(lookback, len(feat))], axis=0) for feat in features], axis=2) # 标签:未来5日平均收益率 y = np.array([data.loc[common_idx].iloc[i:i+5].pct_change().mean().values for i in range(lookback, len(common_idx))]) return X, y
2. 输入输出形状
- 特征张量
X
形状:[样本数, 时间步(60), 资产数(5), 特征数(4)]
- 标签
y
形状:[样本数, 资产数(5)]
(每个样本对应5只资产的未来5日平均收益率)
五、Transformer模型构建:核心架构解析
1. 模型设计目标
- 处理多资产时间序列:同时输入5只资产的历史数据
- 捕捉时间依赖与资产间依赖:通过位置编码和自注意力机制
- 输出多资产收益预测:回归问题,使用MSE损失
2. 关键组件解析
(1)资产嵌入层(Asset Embedding)
将每个资产的4维特征映射到64维隐空间:
self.asset_embed = nn.Linear(n_features=4, d_model=64)
输入形状:(batch, seq_len, assets, features)
→ 输出:(batch, seq_len, assets, d_model)
(2)位置编码(Positional Embedding)
由于Transformer无内置时序信息,需手动添加位置编码:
self.time_pos = nn.Parameter(torch.randn(1, lookback=60, 1, d_model=64)) # 时间位置编码
self.asset_pos = nn.Parameter(torch.randn(1, 1, n_assets=5, d_model=64)) # 资产位置编码
- 通过广播机制与资产嵌入相加,分别捕获时间和资产维度的位置信息。
(3)自定义Transformer编码器层(Custom Transformer Encoder Layer)
继承PyTorch原生层,返回注意力权重以可视化:
class CustomTransformerEncoderLayer(nn.TransformerEncoderLayer): def __init__(self, d_model, nhead, dim_feedforward=256, dropout=0.1): super().__init__(d_model, nhead, dim_feedforward, dropout, batch_first=True) # 显式启用batch_first def forward(self, src, src_mask=None, src_key_padding_mask=None): src2, attn_weights = self.self_attn(src, src, src, need_weights=True) # 获取注意力权重 src = src + self.dropout1(src2) src = self.norm1(src) src2 = self.linear2(self.dropout(self.activation(self.linear1(src)))) src = src + self.dropout2(src2) src = self.norm2(src) return src, attn_weights
(4)维度调整核心逻辑
在进入Encoder前,将张量形状从(batch, seq, assets, d_model)
调整为(batch, seq*assets, d_model)
,以便Encoder处理:
x = x + self.time_pos + self.asset_pos # 叠加位置编码
x = x.permute(0, 1, 3, 2) # 调整维度为 [batch, seq, d_model, assets]
x = x.reshape(batch_size, seq_len * n_assets, d_model) # 合并资产与序列维度
x, attn_weights = self.encoder(x) # Encoder输出形状:(batch, seq*assets, d_model)
x = x.reshape(batch_size, seq_len, n_assets, d_model) # 恢复维度
(5)解码器(Decoder)
提取最后时间步特征,拼接后映射到5维收益空间:
self.decoder = nn.Linear(d_model * n_assets, n_assets) # 输入320维,输出5维
六、模型训练:从数据加载到优化
1. 数据加载器
使用PyTorch的DataLoader处理批量数据:
train_dataset = TensorDataset(torch.FloatTensor(X_train), torch.FloatTensor(y_train))
train_loader = DataLoader(train_dataset, batch_size=64, shuffle=True)
2. 训练配置
- 损失函数:均方误差(MSE)
- 优化器:Adam(学习率
1e-4
) - 训练循环:50个epoch,记录训练/验证损失
model = AssetTransformer(n_features=4, n_assets=5, lookback=60)
criterion = nn.MSELoss()
optimizer = torch.optim.Adam(model.parameters(), lr=0.0001)
七、可视化与结果分析
1. 自注意力矩阵热力图(Cross-Asset Attention Matrix)
提取第一层编码器的注意力权重,展示资产间的依赖关系(以最后时间步为例):
def plot_attention(attention_weights, asset_names): num_assets = len(asset_names) # 取第一个样本、第一个注意力头的权重,形状为 [batch, heads, query, key] attn_matrix = attention_weights[0][0].detach().numpy() # 假设batch=1 attn_matrix = attn_matrix.reshape(num_assets, num_assets) # 恢复资产间注意力矩阵 plt.imshow(attn_matrix, cmap="viridis", aspect="auto") plt.colorbar() plt.title("Cross-Asset Attention Matrix (First Layer)") plt.xlabel("Key Assets") plt.ylabel("Query Assets") plt.xticks(range(num_assets), asset_names) plt.yticks(range(num_assets), asset_names) plt.tight_layout() plt.show()
核心作用:可视化Transformer编码器中资产间的依赖关系,揭示模型如何通过自注意力机制捕捉不同资产的联动效应。
(1)数据提取逻辑
- 注意力权重形状:通过自定义编码器层获取的注意力权重形状为
[batch_size, n_heads, query_length, key_length]
,其中query_length
和key_length
均为时间步×资产数
(本例中为60×5=300
)。 - 关键筛选:聚焦最后一个时间步(当前预测时刻)的资产间注意力,提取最后
n_assets
个查询和键的权重,重塑为[资产数, 资产数]
矩阵(本例中为5×5
),消除时间维度的干扰。
(2)图形元素解析
- 横轴/纵轴:均为资产名称(Asset_0 到 Asset_4),横轴表示“键资产”(Key),纵轴表示“查询资产”(Query)。例如,纵轴Asset_0对应横轴Asset_1的单元格值,表示“资产0在计算时对资产1的关注度”。
- 颜色映射:使用
viridis
色卡,深色(如紫色/蓝色)代表高注意力权重(接近1),浅色(如黄色/白色)代表低权重(接近0)。 - 行业分组验证:
- 同一行业资产(如Tech行业的Asset_0和Asset_1)的交叉区域颜色应更深,表明模型关注行业内协同效应。
- 跨行业资产(如Tech与Energy)的权重可能较低,颜色较浅,符合数据生成时的行业因子设计(行业间波动相关性较低)。
(3)金融意义
- 资产联动捕捉:高权重表示模型认为这两项资产的历史特征对预测当前收益更重要,可能用于构建资产组合时的相关性分析。
- 注意力异常排查:若某资产对自身的注意力权重显著高于其他资产(对角线元素突出),可能意味着模型过度依赖单一资产,存在过拟合风险。
2. 预测结果散点图(Predicted vs Actual Returns)
散点图对比实际收益与预测收益,理想情况下点分布在y=x直线附近:
plt.figure(figsize=(12, 6))
for i in range(5): plt.scatter(y_test[:, i], pred_test[:, i], alpha=0.5, label=f"Asset_{i}")
plt.plot([-0.1, 0.1], [-0.1, 0.1], "k--", label="Perfect Prediction")
plt.title("Predicted vs Actual 5-Day Returns")
plt.xlabel("Actual Returns")
plt.ylabel("Predicted Returns")
plt.legend()
plt.grid(alpha=0.3)
plt.show()
核心作用:评估模型对每只资产未来5日平均收益率的预测精度,直观展示预测值与实际值的偏离程度。
(1)图形结构
- 横轴(X轴):实际收益率(Actual 5-Day Returns),范围约
[-0.1, 0.1]
(覆盖多数金融资产的短期波动区间)。 - 纵轴(Y轴):预测收益率(Predicted Returns),与横轴范围一致,便于对比。
- 散点分布:
- 每个资产(5只)用不同颜色区分(如Asset_0为蓝色,Asset_1为橙色),标签清晰标注。
- 理想情况下,散点应紧密分布在黑色虚线
y=x
附近,表明预测值与实际值接近;散点越偏离虚线,预测误差越大。
(2)量化指标辅助解读
- MSE损失:训练日志中显示的Test Loss(如0.0006)对应散点的整体离散程度,值越小,散点越集中。
- 资产差异:若某资产(如Asset_4)的散点明显偏离对角线,可能是该资产的特质因子噪声较大,或模型对其行业特征的捕捉不足。
(3)实战意义
- 预测可靠性判断:若多数散点位于对角线附近,且各资产分布均匀,说明模型泛化能力较强,可用于实际收益预测;反之,需优化特征工程或调整模型结构。
3. 策略回测累计收益曲线(Long-Short Portfolio Performance)
通过预测结果构建多空策略(买入前20%预测收益资产,卖空后20%),对比策略与市场收益:
def backtest_strategy(pred_returns, data, lookback=60): long_thresh = np.quantile(pred_returns, 0.8, axis=1)[:, None] short_thresh = np.quantile(pred_returns, 0.2, axis=1)[:, None] long_mask = pred_returns >= long_thresh short_mask = pred_returns <= short_thresh # 标准化仓位(多头/空头资产等权分配) position = (long_mask / long_mask.sum(axis=1)[:, None]) - (short_mask / short_mask.sum(axis=1)[:, None]) # 计算收益 returns = data.pct_change().iloc[lookback+1:lookback+1+len(position)] strategy_ret = (position[:-1] * returns.iloc[1:]).sum(axis=1) market_ret = returns.mean(axis=1) return strategy_ret.cumsum(), market_ret.cumsum()
核心作用:验证基于预测结果的多空策略能否获取超额收益,对比策略表现与市场基准。
(1)策略构建逻辑
- 多空筛选:
- 多头:预测收益前20%的资产(高于80%分位数),等权买入。
- 空头:预测收益后20%的资产(低于20%分位数),等权卖空。
- 仓位管理:多头和空头仓位分别标准化(权重和为1),确保风险中性。
(2)曲线元素解析
- 蓝色曲线(Transformer Strategy):
- 若曲线向上倾斜且斜率大于市场曲线,表明策略有效,能通过多空操作获取超额收益。
- 若曲线波动较大,可能受限于模拟数据的高噪声,或需增加风险控制(如止损)。
- 黄色曲线(Market Index):
- 市场平均收益(所有资产等权平均),作为基准线。若策略曲线长期位于其上方,说明模型具备实际应用价值。
(3)金融指标延伸
- 夏普比率:可进一步计算策略的风险调整后收益(假设无风险利率为0),公式为
(策略年化收益) / (策略收益标准差)
,值越高表明风险收益比越好。 - 最大回撤:曲线中的回调幅度,反映策略的抗风险能力,与累计收益结合评估策略稳定性。
4. 可视化总结表
图形类型 | 核心价值 | 理想结果特征 | 常见异常信号 |
---|---|---|---|
注意力热力图 | 资产间依赖关系建模验证 | 同行业资产权重高,跨行业权重低 | 对角线权重异常高(过拟合) |
预测散点图 | 模型预测精度评估 | 散点紧密分布于y=x附近,MSE低 | 某资产散点显著偏离(特征不足) |
回测收益曲线 | 策略有效性验证 | 策略曲线持续跑赢市场,夏普比率>1 | 曲线长期低于市场(模型失效) |
通过这三类图形,可从模型机理(注意力)、预测能力(散点)、实战价值(回测)三个维度全面评估Transformer在多资产收益预测中的表现,为后续优化提供明确方向。
八、常见错误与解决方案
1. Transformer输入维度不匹配
错误信息:TypeError: CustomTransformerEncoderLayer.forward() got an unexpected keyword argument 'is_causal'
原因:未正确处理PyTorch Transformer的隐含参数。
解决:在自定义编码器层的forward
方法中添加is_causal=False
默认参数,兼容框架逻辑。
2. 注意力权重形状错误
错误信息:ValueError: cannot reshape array of size 300 into shape (5,5)
原因:未正确提取最后时间步的注意力权重,误将全序列权重直接重塑。
解决:先筛选最后时间步的查询/键资产权重,再重塑为[资产数, 资产数]
矩阵:
# 正确提取最后时间步(假设序列长度60,资产数5)
last_step_attn = attn_matrix[-5:, -5:] # 取最后5个查询(资产)对最后5个键(资产)的权重
3. 位置编码广播失败
错误信息:RuntimeError: The size of tensor a (60) must match the size of tensor b (1) at non-singleton dimension 1
解决:确保位置编码维度包含资产轴(如[1, lookback, 1, d_model]
),通过广播自动适配资产数。
九、项目总结与扩展方向
1. 项目亮点
- 多维度建模:同时捕捉时间序列依赖(位置编码)和资产间依赖(自注意力)。
- 完整量化流程:从数据生成到策略回测,复现真实量化研究闭环。
- 维度调整详解:通过
view()
/reshape()
处理复杂张量变换,解决PyTorch常见形状问题。
2. 改进方向
- 数据增强:添加滑动窗口、噪声注入等技术提升模型鲁棒性。
- 混合架构:结合CNN提取局部特征,或LSTM捕捉长期趋势。
- 真实场景适配:接入股票/期货高频数据,优化数据预处理流程(如复权处理)。
十、给初学者的建议
- 分步调试:先用小数据集(如
days=100, n_assets=2
)验证代码逻辑,再扩展规模。 - 维度打印:在模型各关键节点添加
print(x.shape)
,确保输入输出形状符合预期。 - 官方文档优先:PyTorch Transformer文档是理解注意力机制的最佳材料,重点关注
batch_first
参数和输入维度要求。
通过本项目,读者可掌握多变量时间序列建模的核心思路,理解Transformer在序列建模中的优势,为后续量化模型开发打下坚实基础。
完整代码
import numpy as np
import pandas as pd
import matplotlib.pyplot as plt
import torch
import torch.nn as nn
from torch.utils.data import DataLoader, TensorDataset
import osos.environ["KMP_DUPLICATE_LIB_OK"] = "TRUE"# ==================== 数据生成与预处理 ====================
def generate_market_data(days=2000, n_assets=5):np.random.seed(42)market = np.cumprod(1 + np.random.normal(0.0003, 0.015, days))assets = []for i in range(n_assets):sector_factor = 0.3 * np.sin(i * 0.8 + np.linspace(0, 10 * np.pi, days))idiosyncratic = np.cumprod(1 + np.random.normal(0.0002, 0.02, days))price = market * (1 + sector_factor) * idiosyncraticassets.append(price)dates = pd.date_range("2015-01-01", periods=days)return pd.DataFrame(np.array(assets).T, index=dates, columns=[f"Asset_{i}" for i in range(n_assets)])data = generate_market_data()# ==================== 特征工程 ====================
def create_features(data, lookback=60):n_assets = data.shape[1]features = []sector_map = {0: "Tech", 1: "Tech", 2: "Consume", 3: "Consume", 4: "Energy"}for i, asset in enumerate(data.columns):df = pd.DataFrame()df["Return"] = data[asset].pct_change()df["Volatility"] = df["Return"].rolling(20).std() * np.sqrt(252)df["MA10"] = data[asset].rolling(10).mean()sector = sector_map[i]sector_cols = [col for col in data.columns if sector_map[int(col.split("_")[1])] == sector]df["Sector_RS"] = data[asset] / data[sector_cols].mean(axis=1)features.append(df.dropna())common_idx = features[0].indexfor df in features[1:]:common_idx = common_idx.intersection(df.index)features = [df.loc[common_idx] for df in features]X = np.stack([np.stack([feat.iloc[i - lookback : i] for i in range(lookback, len(feat))],axis=0,)for feat in features],axis=2,)y = np.array([data.loc[common_idx].iloc[i : i + 5].pct_change().mean().valuesfor i in range(lookback, len(common_idx))])valid_indices = ~np.isnan(y).any(axis=1) & ~np.isinf(y).any(axis=1)return X[valid_indices], y[valid_indices]X, y = create_features(data)
print(f"数据形状: X={X.shape}, y={y.shape}")# ==================== 模型定义 ====================
class CustomTransformerEncoderLayer(nn.TransformerEncoderLayer):"""自定义编码器层以返回注意力权重"""def __init__(self, d_model, nhead, dim_feedforward=256, dropout=0.1, activation="relu"):super().__init__(d_model=d_model,nhead=nhead,dim_feedforward=dim_feedforward,dropout=dropout,activation=activation,batch_first=True,)self.norm1 = nn.LayerNorm(d_model)self.norm2 = nn.LayerNorm(d_model)def forward(self, src, src_mask=None, src_key_padding_mask=None):src2, attn_weights = self.self_attn(src,src,src,attn_mask=src_mask,key_padding_mask=src_key_padding_mask,need_weights=True,)src = src + self.dropout1(src2)src = self.norm1(src)src2 = self.linear2(self.dropout(self.activation(self.linear1(src))))src = src + self.dropout2(src2)src = self.norm2(src)return src, attn_weightsclass AssetTransformer(nn.Module):def __init__(self, n_features, n_assets, lookback, d_model=64, nhead=4):super().__init__()self.asset_embed = nn.Linear(n_features, d_model)self.time_pos = nn.Parameter(torch.randn(1, lookback, 1, d_model))self.asset_pos = nn.Parameter(torch.randn(1, 1, n_assets, d_model))self.final_norm = nn.LayerNorm(d_model)encoder_layer = CustomTransformerEncoderLayer(d_model=d_model, nhead=nhead, dim_feedforward=256)self.encoder = nn.TransformerEncoder(encoder_layer, num_layers=3, norm=nn.LayerNorm(d_model))self.decoder = nn.Linear(d_model * n_assets, n_assets)self.attention_weights = Nonedef forward(self, x):batch_size = x.size(0)seq_len = x.size(1)n_assets = x.size(2)x = self.asset_embed(x)x = x + self.time_pos + self.asset_posx = x.view(batch_size, seq_len * n_assets, -1)attn_weights_list = []for layer in self.encoder.layers:x, attn_weights = layer(x)attn_weights_list.append(attn_weights)self.attention_weights = attn_weights_listx = x.view(batch_size, n_assets, seq_len, -1)x = x[:, :, -1, :]x = x.reshape(batch_size, -1)return self.decoder(x)# ==================== 模型训练 ====================
def train_model(X, y, lookback=60, n_epochs=50):split = int(0.8 * len(X))X_train, X_test = X[:split], X[split:]y_train, y_test = y[:split], y[split:]train_dataset = TensorDataset(torch.FloatTensor(X_train), torch.FloatTensor(y_train))train_loader = DataLoader(train_dataset, batch_size=64, shuffle=True)model = AssetTransformer(n_features=4, n_assets=5, lookback=lookback)criterion = nn.MSELoss()optimizer = torch.optim.Adam(model.parameters(), lr=0.0001)for epoch in range(n_epochs):model.train()total_loss = 0for batch_x, batch_y in train_loader:optimizer.zero_grad()outputs = model(batch_x)loss = criterion(outputs, batch_y)loss.backward()optimizer.step()total_loss += loss.item()model.eval()with torch.no_grad():test_pred = model(torch.FloatTensor(X_test))test_loss = criterion(test_pred, torch.FloatTensor(y_test))print(f"Epoch {epoch + 1}/{n_epochs} | "f"Train Loss: {total_loss / len(train_loader):.4f} | "f"Test Loss: {test_loss:.4f}")return model, y_test, test_pred.detach().numpy()model, y_test, pred_test = train_model(X, y)# ==================== 可视化部分 ====================
def plot_attention(attention_weights, asset_names):plt.figure(figsize=(12, 8))# 取第一个样本、第一个注意力头的注意力权重num_assets = len(asset_names)num_seq = 60 # 假设序列长度为 60layer_weights = attention_weights[0][0, :, : num_assets * num_seq].detach().numpy()layer_weights = layer_weights.reshape(num_assets * num_seq, num_assets * num_seq)# 取每个资产最后一个时间步的注意力权重last_step_weights = layer_weights[-num_assets:, -num_assets:]plt.imshow(last_step_weights, cmap="viridis", aspect="auto")plt.colorbar()plt.title("Cross-Asset Attention Matrix (First Layer)")plt.xlabel("Key Assets")plt.ylabel("Query Assets")plt.xticks(range(num_assets), asset_names)plt.yticks(range(num_assets), asset_names)plt.tight_layout()plt.show()if model.attention_weights:asset_names = data.columns.tolist()plot_attention(model.attention_weights, asset_names)
else:print("未能捕获注意力权重")# 预测结果散点图
plt.figure(figsize=(12, 6))
for i in range(5):plt.scatter(y_test[:, i], pred_test[:, i], alpha=0.5, label=f"Asset_{i}")
plt.plot([-0.1, 0.1], [-0.1, 0.1], "k--")
plt.title("Predicted vs Actual Returns")
plt.xlabel("Actual 5-Day Returns")
plt.ylabel("Predicted Returns")
plt.legend()
plt.grid(alpha=0.3)
plt.show()# ==================== 策略回测 ====================
def backtest_strategy(pred_returns, data, lookback=60):long_thresh = np.quantile(pred_returns, 0.8, axis=1)short_thresh = np.quantile(pred_returns, 0.2, axis=1)long_mask = pred_returns >= long_thresh[:, None]short_mask = pred_returns <= short_thresh[:, None]position = long_mask.astype(float) / long_mask.sum(axis=1)[:, None]position -= short_mask.astype(float) / short_mask.sum(axis=1)[:, None]returns = data.pct_change().iloc[lookback + 1 : lookback + 1 + len(position)]strategy_ret = (position[:-1] * returns.iloc[1:]).sum(axis=1)market_ret = returns.mean(axis=1)return strategy_ret.cumsum(), market_ret.cumsum()strategy_cum, market_cum = backtest_strategy(pred_test, data)plt.figure(figsize=(12, 6))
plt.plot(strategy_cum, label="Transformer Strategy")
plt.plot(market_cum, label="Market Index", alpha=0.7)
plt.title("Long-Short Portfolio Performance")
plt.xlabel("Trading Days")
plt.ylabel("Cumulative Returns")
plt.legend()
plt.grid(alpha=0.3)
plt.tight_layout()
plt.show()
相关文章:
基于Transformer的多资产收益预测模型实战(附PyTorch实现与避坑指南)
基于Transformer的多资产收益预测模型实战(附PyTorch模型训练及可视化完整代码) 一、项目背景与目标 在量化投资领域,利用时间序列数据预测资产收益是核心任务之一。传统方法如LSTM难以捕捉资产间的复杂依赖关系,而Transformer架…...
SQL:MySQL函数:字符串函数
目录 为什么需要字符串函数? 1️⃣ LENGTH(str) — 这个字符串有几个“字节”? 2️⃣ CHAR_LENGTH(str) — 这个字符串有几个“字符”? 3️⃣ TRIM(str) — 把两边的空格剪掉 4️⃣ REPLACE(str, a, b) — 把 a 替换成 b 使用这些函数时…...
C++-缺省参数
缺省参数 缺省参数也叫默认参数 指的是在函数参数的位置,提前定义一个缺省值(即提前定义一个值),当函数接收到参数时,如果定义缺省值的位置未接收到参数,那么这个位置会自动使用缺省值 通过定义缺省参数…...
MySQL 数据库
目录 1. 数据库简介 1.1 使用数据库的必要性 1.2 数据库的基本概念 1.3 经典数据模型 2. MySQL 服务基础 2.1 MySQL 的二进制安装 2.1.1 基础环境准备 2.1.2 二进制安装 2.1.3 设定配置文件 1. 数据库简介 1.1 使用数据库的必要性 使用数据库可以高效且条理分明地存…...
探寻养生新路径,守护健康生活
在忙碌的现代生活中,人们对健康养生的需求愈发迫切。养生不一定要遵循复杂的规则,从一些新颖且实用的方面入手,同样能收获健康的馈赠。 关注肠道菌群的平衡是养生的关键。肠道内居住着数以万亿计的微生物,它们与人体健康息息相…...
平板收银系统、国产系统,鸿蒙系统,小键盘的封装与应用—仙盟创梦IDE
数字小键盘封装 数组小键盘封装是指将与数组小键盘相关的功能、操作、数据等进行整合,形成一个独立的、可复用的模块。封装数组小键盘具有以下几方面重要意义: 提高代码可维护性 降低复杂度:数组小键盘在实际应用中,可能涉及到…...
微软推动智能体协同运作:支持 A2A、MCP 协议
今日凌晨,微软宣布 Azure AI Foundry 和 Microsoft Copilot Studio 两大开发平台支持最新 Agent 开发协议 A2A,并与谷歌合作开发扩大该协议,这一举措对智能体赛道意义重大。 现状与变革意义 当前智能体领域类似战国时代,各家技术…...
《企业级前端部署方案:Jenkins+MinIO+SSH+Gitee+Jenkinsfile自动化实践》
文章目录 前言前端项目CICD时序图一、环境准备1、服务器相关2、Jenkins凭据3、注意事项 二、设计思想1. 模块化设计2.多环境支持3. 制品管理4. 安全部署机制5. 回滚机制 三、CI阶段1、构建节点选择2、代码拉取3、代码编译4、打包并上传至minio 四、CD阶段五、回滚阶段六、构建通…...
数据库的进阶操作
目录 1、数据库的约束 2、查询操作的进阶 2.1 查询插入 2.2 聚合查询 2.3 运算查询 2.3 分组查询 2.4 联合查询 2.5 内外连接 2.6 子查询 2.7 合并查询 1、数据库的约束 数据库的约束是指:数据库会自动的对数据的合法性进行校验和检查的一系列操作的机制&a…...
小刚说C语言刷题—1341银行存款问题
1.题目描述 亮亮把 n 元按照 m 年期整存存入银行,按照目前银行的年利率,请问到期后亮亮可以连本带息总共拿到多少钱? 存期(整存整取) 年利率 1年 3.25% 2年 3.75% 3年∼4 年 4.25% 5年及 5年以上 4.75% 输入…...
15 个 Azure DevOps 场景化面试问题及解答
问题 1. 解释 Azure DevOps YAML 管道的典型结构。 您可以从管道的整体结构开始,从触发器开始。您也可以选择解释它可能包含的不同类型的阶段:构建、测试、扫描、部署等。 Azure DevOps YAML 管道结构示例 触发器指示管道运行。它可以是持续集成 (CI) 或…...
spring cloud 跨服务调用
微服务将不同功能模块拆分成多个不同的服务,在业务逻辑集成时候,难免会有一个服务需要依赖调用另一个服务的情况。如订单服务需要通过用户服务查询用户相关信息,这时候微服务之间就需要进行跨服务调用。 要想进行跨服务调用,服务…...
手机隐私数据彻底删除工具:回收或弃用手机前防数据恢复
软件介绍 有这样一款由吾爱网友chenwangjun 原创开发的数据处理软件,名为 AndroidDiskClear。它的核心功能十分强大,能够将你手机里已经删除的各类文件,像图片、普通文件、文字信息等彻底清除干净,有效杜绝数据恢复类软件的二次恢…...
【Electron】electron-vue 借助 element-ui UI 库助力桌面应用开发
前面文章我们讲过 electron 让可以用 HTML、JS、CSS 开发桌面应用程序。而 electron-vue 是一个结合了 electron 与 vue 的套件。这样我们就能方便地使用 vue 快速开发桌面应用。但是,vue 只是在 js 这层面做了大量的便捷的操作。对 UI 并未过多涉及。此时如果您在开…...
《信息论与编码课程笔记》——信源编码(1)
目录 一、信源编码基本概念 1. 定义与目的 2. 编码示例 3. 编码分类 4. 唯一可译码的判断标准 5. 编码评价指标 二、香农第一定理(无失真可变长信源编码定理) 1. 核心内容 2. 关键概念与指标 3. 数据压缩的本质 4. 例子与启示 5. 定理的意义…...
2022年8月,韩先超对中移信息进行微服务架构原理(Docker+k8s+DevOps+Go等)培训
2022年8月,韩先超对中移信息进行微服务架构原理(Dockerk8sDevOpsGo等)培训 2022年8月,在企业数字化转型和云原生架构加速演进的背景下, 中移信息技术有限公司特别邀请云原生与DevOps领域专家 韩先超老师,…...
【PostgreSQL数据分析实战:从数据清洗到可视化全流程】8.4 数据故事化呈现(报告结构设计/业务价值提炼)
👉 点击关注不迷路 👉 点击关注不迷路 👉 点击关注不迷路 文章大纲 8.4 数据故事化呈现:从报告结构到业务价值的深度融合一、数据故事化的核心价值体系(一)报告结构设计的黄金框架1. 业务场景锚定ÿ…...
Docker 核心目录结构
1. Docker 核心目录结构 数据存储目录 默认根目录:/var/lib/docker Docker 所有运行时数据(镜像、容器、卷、网络配置等)的默认存储位置。 bash 复制 下载 # 查看 Docker 数据根目录 docker info | grep "Docker Root Dir" # 输出…...
【工具推荐】Code2Prompt
DeepWiki工具可以帮我们快速理解 GitHub 项目,简直是理解陌生开源项目的利器! 但是,它有个小小的“遗憾”——只能解析在线的 GitHub 项目。 如果是本地项目怎么办,还要特意上传,no,code2prompt 就是一款…...
OpenCV定位地板上的书
任务目标是将下面的图片中的书本找出来: 使用到的技术包括:转灰度图、提取颜色分量、二值化、形态学、轮廓提取等。 我们尝试先把图片转为灰度图,然后二值化,看看效果: 可以看到,二值化后,书的…...
Spring Cloud:概述,服务注册和服务发现,多机部署和负载均衡
什么是微服务 就是将一个大型的应用程序拆分成多而小的独立的服务模块,每个服务模块围绕某个业务功能建立,具有独立的数据库,服务栈,并通过轻量级的通信协议进行交互。 单体架构 就是将所有的业务和功能都打包在一个jar包中&…...
Linux的基础开发工具
目录 前言: 1、包管理器yum 1.1 软件包的依赖 1.2 镜像源 1.3 查找/安装/卸载软件 2、编辑器vim 2.1 命令模式(默认) 2.1.1 撤销与反撤销 2.1.2 光标定位 2.1.3 复制&&剪切(删除)&&粘贴 2.1.4 替换 2.1.5 插入模式 2.1.6 V-Block模式 …...
解锁跨平台开发的新时代——Compose Multiplatform
解锁跨平台开发的新时代——Compose Multiplatform 在当今移动和桌面应用程序开发领域,跨平台解决方案是开发者们梦寐以求的工具。而由JetBrains打造的Compose Multiplatform正是这样一款现代UI框架,它基于Kotlin技术,为开发者构建高性能且美观的用户界面提供了极大的便利和…...
键盘固件刷写详解:Bootloader
键盘固件刷写详解:从入门到精通 引言 作为一名机械键盘爱好者,相信大家都曾经面临过刷写固件的问题。无论是想要自定义按键功能,还是升级键盘的固件,掌握刷写技巧都是非常必要的。本文将全面介绍不同类型的引导加载程序…...
网络原理初识
本来想从网络的发展史开始写,之后再写网络的定义啥的,但快写完了才发现,这不课本教材吗,没劲,遂弃之,重撰,删芜就简.写点我认为,对程序员来说真正有用的东西 目录 IP地址 概念 格式 特殊地址 端口号 概念 格式 协议 概念 知名协议的默认端口 五元组 协议分层 OS…...
PVP鼠标推荐(deepseek)
下面有不懂的自行百度查找👍 ❤️ 以下是几款在 双击性能(DBC) 和 拖拽点击(DC) 方面表现优秀的游戏鼠标推荐,结合了硬件性能、微动寿命以及玩家口碑: 1. 罗技 G102/G203 Lightsync 特点&#…...
Navee滑板车强势登陆中国,以智能科技重塑城市出行新风尚
当科技与潮流邂逅,城市出行迎来了一场前所未有的变革。全球智能出行领域的先锋品牌Navee,携其多款旗舰滑板车产品——ST3、GT3、V系列等,正式进军中国市场。凭借“颜值、性能、安全、智能”四大核心优势,Navee正以破竹之势重新定义…...
【Linux网络】网络命令
Ping Ping命令是一种用于测试网络连接状况的工具,在Windows、Linux、macOS等操作系统中都可以使用。以下是其详细介绍: 它主要通过向目标主机发送Internet控制报文协议(ICMP)的回声请求(Echo Request)数据…...
【JS逆向基础】面向对象
1,OOP编程思想 面向对象编程是在面向过程编程的基础上发展来的,它比面向过程编程具有更强的灵活性和扩展性。面向对象编程是程序员发展的分水岭,很多初学者会因无法理解面向对象而放弃学习编程。 面向对象编程(Obiect:oriented Programming…...
# 如何使用OpenCV进行发票的透视变换和二值化处理
如何使用OpenCV进行发票的透视变换和二值化处理 引言 在自动化处理发票和其他文档时,图像预处理是一个关键步骤,它可以帮助提高OCR(光学字符识别)的准确性。透视变换用于校正图像中的透视失真,而二值化处理则可以简化…...
NetSuite 如何得到所有Item最近一次采购订单的货品单价?
我们知道,如果取Item主数据的数据,得到的是很多相关transaction的Item 货品平均价值;如果只想得到最近一次采购订单上的采购单价,主数据上应该无法直接得到,那该如何处理?我们可以利用Saved Search来拉取相…...
[5-2] 对射式红外传感器计次旋转编码器计次 江协科技学习笔记(38个知识点)
1、 2、 3、这些缩写通常出现在嵌入式系统或微控制器的上下文中,它们各自有不同的功能: • RCC:Reset and Clock Control(复位和时钟控制)。它负责管理微控制器的时钟系统,包括时钟源的选择、时钟频率的设置…...
阿里云 golang 一面
消息队列 分布式文件系统 Linux的虚拟网络设备 TCP SSL/TLS 场景:对于打车场景,如何设计一个系统来处理并发打车请求? 设计一个处理并发打车请求的系统需要考虑多个方面,包括系统架构、数据存储、负载均衡和实时性。以下是一个基…...
【C++ Qt】多元素控件(ListWidget、TableWidget、TreeWidget)
每日激励:“不设限和自我肯定的心态:I can do all things。 — Stephen Curry” 绪论: 本章将通过代码示例详细介绍了Qt中QListWidget、QTableWidget和QTreeWidget三种多元素控件的使用方法与核心功能,涵盖列表的增删操作、表格…...
排序算法-冒泡排序
冒泡排序一种简单的排序算法。它重复地走访过要排序的数列,一次比较两个元素,如果它们的顺序错误就把它们交换过来。走访数列的工作是重复地进行直到没有再需要交换,也就是说该数列已经排序完成。 这个算法的名字由来是因为元素会经由交换慢…...
C++ 工具链与开发实践:构建安全、高效与创新的开发生态
引言 在 C 的技术演进中,工具链的革新与开发实践的迭代始终是推动语言生命力的核心动力。从内存安全的攻防体系到嵌入式设备的能效优化,从跨平台开发的降本增效到开发者社区的生态构建,C 正通过工具链与方法论的双重升级,应对复杂…...
uni-pages-hot-modules插件:uni-app的pages.json的模块化及模块热重载
uni-pages-hot-modules uni-app的pages.json的模块化及模块热重载 uni-app的pages.json的模块化及模块热重载 解决uni-app的pages.json无法模块化的问题,并且解决模块热重载和缓存的问题 安装 npm i uni-pages-hot-modules -Suni-app vite版本(vue3)示例项目 uni-app webpa…...
【WEB3】区块链、隐私计算、AI和Web3.0——数据民主化(1)
区块链、隐私计算、AI,是未来Web3.0至关重要的三项技术。 1.数据民主化问题 数据在整个生命周期(生产、传输、处理、存储)内的隐私安全,则是Web3.0在初始阶段首要解决的问题。 数据民主化旨在打破数据垄断,让个体能…...
从0到1:用Lask/Django框架搭建个人博客系统(4/10)
摘要:本文深入解析了使用Lask和Django构建个人博客系统的全过程。从技术选型的考量,突出Lask的轻量灵活与Django的强大功能,到开发环境搭建、项目初始化,再到核心功能实现如文章管理、用户认证与权限控制,详细阐述了开…...
探索 C++23 的 views::cartesian_product
文章目录 一、背景与动机二、基本概念与语法三、使用示例四、特点与优势五、性能与优化六、与 P2374R4 的关系七、编译器支持八、总结 C23 为我们带来了一系列令人兴奋的新特性,其中 views::cartesian_product 是一个非常实用且强大的功能,它允许我们轻…...
微软拼音自定义词库方法
1.准备文件input.txt 令狐冲 郭靖2.使用python转成微软拼音需要的格式 from xpinyin import Pinyin # pip install xpinyin -i https://pypi.tuna.tsinghua.edu.cn/simplep Pinyin()with open(input.txt, r, encodingutf-8) as infile:with open(output.txt, w, encodingutf…...
SQLite3常用语句汇总
SQLite 命令行工具(sqlite3 shell) 中的内置命令 命令作用说明.open filename.db打开或创建一个 SQLite 数据库文件.tables列出当前数据库中的所有表.schema [table]查看某个表或所有表的建表语句(DDL).headers ON/OFF开启或关闭…...
数据库设计三范式
第一范式 (1NF) 每个表中的每一列都是原子值 每个表中的每一行都是唯一的 下面这个表格就是不符合第一范式的例子:因为学时数能够拆分为讲课和实验。所以他并不是每一列都是原子值。 不仅要考虑列是否是原子值,还需要考虑每列的值是否可拆分。 第二范…...
GoWeb开发
学习目标: 本篇要达到的目的,能为后续复习提供极大便利。 (当我写下本篇博客时,已复习3遍) 一、网络通信概述 (为本篇基础核心内容) 1、什么是网络通信? 网络通信是指不同设备&…...
(7)Nokov 室内光学跟踪系统
文章目录 前言 7.1 所需硬件 7.2 Nokov 系统设置 7.3 配置旋翼机 7.4 启动 Nokov 模块 7.5 MAVProxy 准备 7.6 测试飞行 7.7 参数说明 前言 本文将介绍如何通过 Nokov 运动捕捉系统向旋翼机传输姿势信息。联系方式:NOKOV | Optical Motion Capture System。…...
Linux Shell编程和循环语句
一.for循环语句 1.for语句的结构2.for循环语句实例①根据姓名列表来批量创建多个用户②根据IP地址列表检查主机状态 二.使用while循环语句1.while语句结构2.while循环语句应用①批量添加规律编号用户②猜价格游戏 三 until循环语句1.until语句结构① 计算1-50的和 1.for语句的结…...
Java后端程序员学习前端之JavaScript
1.什么是JavaScript 1.1.概述 JavaScript是一门世界上最流行的脚本语言javaScript 一个合格的后端人员,必须要精通JavaScript 1.2.历史 JavaScript的起源故事-CSDN博客 2.快速入门 2.1.引入JavaScript 1.内部标签 <script>//.......</script> --…...
redis多路复用IO模型 以及 6.0引入的多线程模型
redis为什么选择单线程 采用多线程的话,会出现上下文切换的开销采用多线程,会带来共享资源的竞争控制,比如多个线程同时访问同一个资源(键值)时,需要额外的手段来保障共享资源的正确性,会带来额…...
101alpha_第6个
第6个alpha (-1 * correlation(open, volume, 10)) 这个就是看这两个相似性。10天之内的 如果结果为正且数值较大,投资者可能会认为在开盘价上涨时成交量萎缩,市场上涨动力不足,可能是卖出信号;反之,开盘价下跌时成交…...
crawl4ai能替代scrapy等传统爬虫框架吗?
传统爬虫框架就像拿着渔网在数字海洋中捕鱼——虽然能捞到东西,但面对现代网站的复杂性时常常"漏网之鱼"满天飞。以Scrapy为代表的工具存在三大致命短板:首先是JavaScript盲区,对动态渲染内容束手无策,就像试图用收音机…...