主题切换
10.2 生成式AI
概念详解
生成式AI(Generative AI)在量化金融中正在打开全新的可能性。与传统的判别式模型(如预测收益率方向)不同,生成模型学习数据的底层分布,能够生成与真实数据统计特征相似的新样本。这一能力在量化金融中有着多种重要应用。
生成式AI在量化金融中的主要应用方向:
1. 合成数据生成(Synthetic Data Generation) 金融数据的历史样本量通常不足以训练复杂的深度学习模型(尤其是对于低频策略,独立样本数可能只有几百个)。生成模型可以通过学习真实数据的分布来生成无限的"伪样本",用于:
- 增强训练数据:扩充有限的训练集,缓解过拟合
- 压力测试:生成极端市场场景的样本,评估策略的尾部风险
- 隐私保护:生成去标识化的合成数据集,在不暴露真实客户数据的前提下进行模型开发
2. 市场情景生成(Scenario Generation) 生成模型可以学习市场在不同Regime下的联合分布,生成各种可能的未来路径(而非单一的点预测),用于:
- 风险管理的蒙特卡洛模拟
- 投资组合压力测试
- 期权定价的改进
3. 异常检测(Anomaly Detection) 通过训练生成模型学习"正常"市场行为的分布,当新的数据落在低概率区域时,将其标记为异常。这在检测市场操纵、闪崩预警等场景中有应用。
4. 大语言模型(LLM)辅助量化
- 自动化研报分析:解析分析师报告、电话会议记录中的关键信息
- 因子挖掘:通过自然语言指令自动生成和测试候选因子
- 代码生成:辅助编写和调试量化策略代码
- 知识提取:从学术论文中提取可测试的量化策略
核心生成模型类型:
- GAN(生成对抗网络):通过生成器与判别器的博弈学习数据分布,擅长生成逼真的金融时间序列
- VAE(变分自动编码器):学习数据的概率潜在表示,可用于生成和异常检测
- Diffusion Models:通过逐步去噪生成数据,在图像生成领域取得突破后,正在被应用到时间序列生成中
- GPT/LLM:基于Transformer的生成式语言模型,在文本分析和代码生成中有广泛应用
名词解释:GAN
生成对抗网络,通过生成器与判别器的博弈来学习真实数据的分布,可生成逼真的金融时间序列用于数据增强。
名词解释:扩散模型(Diffusion Models)
一类生成模型,通过逐步向数据添加噪声(前向过程)然后学习反向去噪(逆向过程)来生成新样本。在金融时间序列生成中展现出优于GAN的稳定性和多样性。
名词解释:变分自动编码器(VAE)
一种将输入编码为概率分布(而非确定性的点)的自动编码器,可以生成具有连续性和多样性的新样本。
数学原理
GAN的博弈框架
GAN由两个神经网络组成:生成器
$$\min_G \max_D \; \mathbb{E}_{x\sim p_{data}}[\log D(x)] + \mathbb{E}_{z\sim p_z}[\log(1 - D(G(z)))]$$
- 生成器
将随机噪声 映射为"假"数据 - 判别器
判断输入 是真实数据(输出接近1)还是生成数据(输出接近0) 希望最大化 (让判别器犯错), 希望正确区辨真假
VAE的ELBO目标
VAE通过最大化证据下界(ELBO)来学习:
$$\mathcal{L}_{ELBO} = \mathbb{E}_{q_\phi(z|x)}[\log p_\theta(x|z)] - D_{KL}(q_\phi(z|x) \| p(z))$$
第一项是重建误差(生成的样本应尽可能像原始输入),第二项是KL散度(后验分布应尽可能接近先验)。
扩散模型的前向与反向过程
前向过程(加噪):
$$q(x_t|x_{t-1}) = \mathcal{N}(x_t; \sqrt{1-\beta_t}x_{t-1}, \beta_t I)$$
反向过程(去噪):
$$p_\theta(x_{t-1}|x_t) = \mathcal{N}(x_{t-1}; \mu_\theta(x_t, t), \Sigma_\theta(x_t, t))$$
训练目标是预测每一步添加的噪声:
$$\mathcal{L} = \mathbb{E}_{t, x_0, \epsilon}\left[\|\epsilon - \epsilon_\theta(x_t, t)\|^2\right]$$
Python实战
📌 案例1:GAN结构示意
此处有展示代码展开 ▼
python
generator = build_generator()
discriminator = build_discriminator()
# 对抗训练循环点击展开可浏览运行结果
📘 本段为公式演示片段(伪代码占位符,无完整上下文)
📌 案例2:金融时间序列GAN(TimeGAN概念实现)
此处有展示代码展开 ▼
python
import numpy as np
import torch
import torch.nn as nn
import torch.optim as optim
from torch.utils.data import DataLoader, TensorDataset
class FinancialGAN:
"""
简化版金融时间序列GAN
生成器: 从噪声生成收益率序列
判别器: 区辩真实 vs 生成的收益率序列
"""
def __init__(self, seq_length=60, feature_dim=5, latent_dim=32):
self.seq_length = seq_length
self.feature_dim = feature_dim
self.latent_dim = latent_dim
# 生成器
self.generator = nn.Sequential(
nn.Linear(latent_dim, 128),
nn.LeakyReLU(0.2),
nn.Linear(128, 256),
nn.LeakyReLU(0.2),
nn.Linear(256, seq_length * feature_dim),
nn.Tanh() # 输出范围 [-1, 1]
)
# 判别器
self.discriminator = nn.Sequential(
nn.Linear(seq_length * feature_dim, 256),
nn.LeakyReLU(0.2),
nn.Dropout(0.3),
nn.Linear(256, 128),
nn.LeakyReLU(0.2),
nn.Dropout(0.3),
nn.Linear(128, 1),
nn.Sigmoid()
)
self.g_optimizer = optim.Adam(self.generator.parameters(), lr=0.0002, betas=(0.5, 0.999))
self.d_optimizer = optim.Adam(self.discriminator.parameters(), lr=0.0002, betas=(0.5, 0.999))
self.criterion = nn.BCELoss()
def train(self, real_data, n_epochs=100, batch_size=32):
"""训练GAN"""
real_data = torch.FloatTensor(real_data)
dataset = TensorDataset(real_data)
dataloader = DataLoader(dataset, batch_size=batch_size, shuffle=True)
for epoch in range(n_epochs):
for batch_idx, (real_batch,) in enumerate(dataloader):
batch_size_actual = real_batch.size(0)
# 真实标签为1,虚假标签为0
real_labels = torch.ones(batch_size_actual, 1)
fake_labels = torch.zeros(batch_size_actual, 1)
# === 训练判别器 ===
self.d_optimizer.zero_grad()
# 真实数据损失
real_output = self.discriminator(real_batch.view(batch_size_actual, -1))
d_real_loss = self.criterion(real_output, real_labels)
# 虚假数据损失
noise = torch.randn(batch_size_actual, self.latent_dim)
fake_data = self.generator(noise)
fake_output = self.discriminator(fake_data.detach().view(batch_size_actual, -1))
d_fake_loss = self.criterion(fake_output, fake_labels)
d_loss = d_real_loss + d_fake_loss
d_loss.backward()
self.d_optimizer.step()
# === 训练生成器 ===
self.g_optimizer.zero_grad()
noise = torch.randn(batch_size_actual, self.latent_dim)
fake_data = self.generator(noise)
fake_output = self.discriminator(fake_data.view(batch_size_actual, -1))
g_loss = self.criterion(fake_output, real_labels) # 生成器希望被判定为真
g_loss.backward()
self.g_optimizer.step()
if epoch % 20 == 0:
print(f"Epoch {epoch}: D_loss={d_loss.item():.4f}, G_loss={g_loss.item():.4f}")
def generate(self, n_samples):
"""生成合成金融时间序列"""
self.generator.eval()
with torch.no_grad():
noise = torch.randn(n_samples, self.latent_dim)
synthetic = self.generator(noise)
synthetic = synthetic.view(n_samples, self.seq_length, self.feature_dim)
return synthetic.numpy()
# ===== 示例:使用GAN生成合成收益率数据 =====
np.random.seed(42)
n_sequences = 500
seq_len = 60
n_features = 5
# 生成真实数据(模拟多资产收益率)
real_returns = np.random.randn(n_sequences, seq_len, n_features) * 0.01
# 加入一些真实数据的统计特征:波动率聚类
for i in range(n_sequences):
regime = np.random.choice([0.5, 1.0, 2.0], p=[0.2, 0.6, 0.2])
real_returns[i] *= regime
# 训练GAN(概念演示)
print("金融时间序列GAN演示:")
print(f" 真实数据形状: {real_returns.shape}")
print(f" 生成器将在 {seq_len * n_features} 维空间中学习\n")
gan = FinancialGAN(seq_length=seq_len, feature_dim=n_features, latent_dim=32)
# gan.train(real_returns, n_epochs=200) # 实际训练时取消注释
# synthetic_data = gan.generate(100)
#
# # 验证生成数据的统计特征
# print("生成数据的统计验证:")
# print(f" 真实数据均值: {real_returns.mean():.6f}")
# print(f" 合成数据均值: {synthetic_data.mean():.6f}")
# print(f" 真实数据标准差: {real_returns.std():.6f}")
# print(f" 合成数据标准差: {synthetic_data.std():.6f}")
print("GAN模型结构已创建,取消注释以运行训练")点击展开可浏览运行结果
金融时间序列GAN演示: 真实数据形状: (500, 60, 5) 生成器将在 300 维空间中学习
📌 案例3:LLM辅助因子挖掘
此处有展示代码展开 ▼
python
def llm_factor_generation_example():
"""
使用LLM自动生成候选因子代码
实际应用中,可以通过API调用GPT-4/Claude等LLM,
根据自然语言描述生成因子计算代码
"""
prompt_template = """
请根据以下因子描述,生成Python代码来计算这个量化因子:
因子名称: {factor_name}
因子描述: {description}
数据变量: df (pandas DataFrame, columns包括: open, high, low, close, volume)
要求:
1. 返回一个pandas Series作为因子值
2. 处理缺失值和异常值
3. 在代码注释中说明计算逻辑
"""
examples = [
{
'factor_name': 'Amihud非流动性',
'description': '衡量股票的流动性不足程度,定义为日收益率的绝对值除以日成交金额的平均值。非流动性越高,交易成本越大。',
},
{
'factor_name': '价格延迟因子',
'description': '衡量价格对市场信息反应的延迟程度。将股票日收益率对同期和滞后N期的市场收益率做回归,计算滞后项回归系数之和占总回归系数的比例。',
},
{
'factor_name': '日内动量',
'description': '用开盘后第一个小时的收益率(开盘价到10:30的价格变化)预测最后一个小时的收益率(14:00到收盘的价格变化)。使用过去M天的数据进行统计。',
}
]
print("LLM辅助因子挖掘示例:")
for ex in examples:
print(f"\n 因子: {ex['factor_name']}")
print(f" 描述: {ex['description']}")
print(f" [提示词已生成,调用LLM API即可获得因子代码]")
llm_factor_generation_example()点击展开可浏览运行结果
LLM辅助因子挖掘示例: 因子: Amihud非流动性 描述: 衡量股票的流动性不足程度,定义为日收益率的绝对值除以日成交金额的平均值。非流动性越高,交易成本越大。 [提示词已生成,调用LLM API即可获得因子代码] 因子: 价格延迟因子 描述: 衡量价格对市场信息反应的延迟程度。将股票日收益率对同期和滞后N期的市场收益率做回归,计算滞后项回归系数之和占总回归系数的比例。 [提示词已生成,调用LLM API即可获得因子代码] 因子: 日内动量 描述: 用开盘后第一个小时的收益率(开盘价到10:30的价格变化)预测最后一个小时的收益率(14:00到收盘的价格变化)。使用过去M天的数据进行统计。 [提示词已生成,调用LLM API即可获得因子代码]
📌 案例4:使用生成模型进行压力测试
此处有展示代码展开 ▼
python
def generate_extreme_scenarios(n_scenarios=1000, base_mu=0.0005, base_sigma=0.01):
"""
使用参数化方法生成极端市场场景用于压力测试
该方法利用Copula + 尾部依赖来生成多资产极端共跌场景
"""
from scipy.stats import norm, t as t_dist
results = []
for i in range(n_scenarios):
# 生成相关性矩阵(随机但保持正定性)
n_assets = 5
random_corr = np.random.uniform(0.3, 0.8, size=(n_assets, n_assets))
random_corr = (random_corr + random_corr.T) / 2
np.fill_diagonal(random_corr, 1.0)
try:
L = np.linalg.cholesky(random_corr)
except np.linalg.LinAlgError:
continue
# 使用t-Copula(胖尾)生成联合场景
df = 3 # 自由度=3,尾部很厚
z = np.random.randn(n_assets)
u = L @ z
# t分布缩放(引入共线性极端事件)
chi = np.random.chisquare(df) / df
scenarios = u / np.sqrt(chi) * base_sigma * 3 # 3倍波动
results.append({
'scenario_id': i,
'returns': scenarios,
'max_loss': scenarios.min(),
'correlation': random_corr[0, 1],
'total_shock': np.abs(scenarios).sum()
})
# 找出Top 10最极端的场景
results.sort(key=lambda x: x['total_shock'], reverse=True)
print("极端场景压力测试:")
print("-" * 50)
for i, r in enumerate(results[:5]):
print(f"场景 {r['scenario_id']}: 最大跌幅={r['max_loss']:.2%}, "
f"总冲击={r['total_shock']:.2%}")
return results
extreme_scenarios = generate_extreme_scenarios(500)点击展开可浏览运行结果
极端场景压力测试: -------------------------------------------------- 场景 202: 最大跌幅=-32.87%, 总冲击=113.86% 场景 92: 最大跌幅=18.29%, 总冲击=112.30% 场景 297: 最大跌幅=-22.80%, 总冲击=78.66% 场景 112: 最大跌幅=-22.19%, 总冲击=71.06% 场景 486: 最大跌幅=-19.22%, 总冲击=69.61%
常见误区
误区1:GAN生成的金融数据可以替代真实数据。 GAN生成的数据可以复现真实数据的统计特征(均值、方差、自相关等),但不能创造真实数据中没有的信息。使用GAN数据进行回测可能严重低估真实风险。
误区2:LLM可以直接用来选股。 LLM虽然在文本理解方面表现出色,但它们不是为数值预测设计的。LLM更适合作为量化工作流中的"辅助工具"(代码生成、文本分析、因子描述),而非"决策引擎"。
误区3:生成的合成数据越多越好。 数据增强有边际递减效应。1000个真实样本+9000个合成样本的训练效果,通常不如1000个真实样本+合适的正则化。合成数据主要用于极端场景的覆盖,而非简单堆量。
误区4:生成模型的评估容易。 判断生成数据"好不好"是困难的。视觉检查、分布匹配度量(MMD)、自相关函数比较等都不完美。金融时间序列的细微结构(如波动率聚类的精确模式)很容易被度量的平均行为掩盖。
误区5:LLM生成的策略代码可以直接实盘使用。 LLM生成的代码可能包含逻辑错误、前视偏差、或使用了已过时的API。所有LLM生成的代码都需要经过严格的代码审查、回测验证和模拟交易测试。
实战练习
练习1: 实现一个简化版的TimeGAN(时间序列GAN)来生成股票收益率序列:
- 使用LSTM作为生成器和判别器的核心
- 在训练中同时优化监督损失(前后时刻的一致性)和对抗损失
- 生成的序列与真实序列比较:均值、标准差、偏度、峰度、自相关函数、波动率聚类
- 分析:生成数据在哪一维度上最难"骗过"判别器?
练习2: 使用LLM API(如OpenAI GPT-4或Anthropic Claude)实现以下自动化流水线:
- Step 1:给定一篇学术论文标题,自动生成该论文提出的因子计算代码
- Step 2:自动生成该因子的回测代码
- Step 3:分析回测结果并生成评估报告
- 评估:LLM生成的代码质量如何?需要进行多少人工修正?
练习3: 训练一个VAE模型用于市场异常检测:
- 使用真实交易日数据训练VAE
- 计算每个交易日的重建误差(reconstruction error)
- 设定阈值,标记重建误差异常高的交易日
- 检查这些异常交易日是否对应已知的市场事件(闪崩、政策冲击)
延伸阅读
- Goodfellow, I., et al. (2014). Generative Adversarial Nets. NeurIPS. — GAN的原始论文。
- Yoon, J., et al. (2019). Time-series Generative Adversarial Networks. NeurIPS. — TimeGAN的原始论文,专门用于时间序列生成。
- Wiese, M., et al. (2020). Quant GANs: Deep Generation of Financial Time Series. Quantitative Finance, 20(9), 1419-1440. — GAN在金融时间序列生成中的应用。
- Assefa, S. A., et al. (2020). Generating Synthetic Data in Finance: Opportunities, Challenges and Pitfalls. SSRN. — 合成数据在金融中的机遇与陷阱。
- Lopez-Lira, A., & Tang, Y. (2023). Can ChatGPT Forecast Stock Price Movements? Return Predictability and Large Language Models. SSRN. — LLM预测股票收益的最新研究。
本章要点
- GAN和VAE是金融合成数据生成的主要工具,可用于数据增强和压力测试
- 生成模型的输出不能替代真实数据——合成数据只能复现统计特征,不能创造真实Alpha
- 大语言模型(LLM)在量化工作流中非常有价值,但最适合作为辅助工具而非决策引擎
- 扩散模型(Diffusion Models)在时间序列生成中展现出优于GAN的潜力
- 生成数据的质量评估比生成本身更困难——细微的结构差异可能在回测中被放大
- 所有AI生成的代码或信号都需要严格的验证流程才能进入实盘