feat(data): 添加个股资金流向接口并重构速率限制配置
- 新增 moneyflow 资金流向数据同步模块 - 实现接口级速率限制配置(sync_config.py) - 更新流动性相关因子定义 - 添加非对称量化损失函数
This commit is contained in:
@@ -84,3 +84,65 @@ class EnsembleQuantLoss(nn.Module):
|
||||
total_loss += self.alpha * h_loss + (1.0 - self.alpha) * ic_loss
|
||||
|
||||
return total_loss / self.ensemble_size
|
||||
|
||||
|
||||
class AsymmetricQuantLoss(nn.Module):
|
||||
"""Asymmetric Quant Loss (非对称 Huber + IC)
|
||||
|
||||
保留全截面计算以维持稳定梯度的前提下,对多头关注的错误进行加权惩罚:
|
||||
1. 过度高估烂股票 (买入陷阱) -> 加重惩罚
|
||||
2. 严重低估好股票 (错失金股) -> 加重惩罚
|
||||
"""
|
||||
|
||||
def __init__(self, alpha: float = 0.5, ensemble_size: int = 32):
|
||||
super().__init__()
|
||||
self.alpha = alpha
|
||||
self.ensemble_size = ensemble_size
|
||||
# 我们不使用内置的 HuberLoss,而是手动写以支持 element-wise 加权
|
||||
self.delta = 1.0 # Huber threshold (可以设得比较小,比如对于收益率设为 0.05)
|
||||
|
||||
def forward(self, preds: torch.Tensor, target: torch.Tensor) -> torch.Tensor:
|
||||
batch_size = preds.shape[0]
|
||||
total_loss = 0.0
|
||||
|
||||
if batch_size < 10:
|
||||
# 极小 batch 退回简单 MSE
|
||||
return ((preds - target.unsqueeze(1)) ** 2).mean()
|
||||
|
||||
target_mean = target.mean()
|
||||
target_std = target.std(unbiased=False) + 1e-8
|
||||
target_norm = (target - target_mean) / target_std
|
||||
|
||||
for i in range(self.ensemble_size):
|
||||
pred_i = preds[:, i]
|
||||
|
||||
# --- 1. 非对称 Huber 损失 ---
|
||||
error = pred_i - target
|
||||
abs_error = torch.abs(error)
|
||||
|
||||
# 标准 Huber 计算
|
||||
quadratic = torch.clamp(abs_error, max=self.delta)
|
||||
linear = abs_error - quadratic
|
||||
base_huber = 0.5 * quadratic**2 + self.delta * linear
|
||||
|
||||
# 【核心逻辑】:非对称权重
|
||||
# 如果 target > 0 且 pred < target (错过了涨的好票) -> 权重 1.5
|
||||
# 如果 target < 0 且 pred > 0 (把跌的票预测成了涨的,容易实盘买入踩雷) -> 权重 2.0
|
||||
# 其他情况 (比如烂票预测得更烂) -> 权重 1.0 正常学习
|
||||
weights = torch.ones_like(target)
|
||||
weights[(target > 0) & (error < 0)] = 1.5
|
||||
weights[(target < 0) & (pred_i > 0)] = 2.0
|
||||
|
||||
weighted_huber = (base_huber * weights).mean()
|
||||
|
||||
# --- 2. IC 损失 (维持全截面排序能力) ---
|
||||
pred_mean = pred_i.mean()
|
||||
pred_std = pred_i.std(unbiased=False) + 1e-8
|
||||
pred_norm = (pred_i - pred_mean) / pred_std
|
||||
|
||||
ic = (pred_norm * target_norm).mean()
|
||||
ic_loss = 1.0 - ic
|
||||
|
||||
total_loss += self.alpha * weighted_huber + (1.0 - self.alpha) * ic_loss
|
||||
|
||||
return total_loss / self.ensemble_size
|
||||
|
||||
@@ -19,7 +19,10 @@ from tabm import TabM
|
||||
|
||||
from src.training.components.base import BaseModel
|
||||
from src.training.components.models.cross_section_sampler import CrossSectionSampler
|
||||
from src.training.components.models.ensemble_quant_loss import EnsembleQuantLoss
|
||||
from src.training.components.models.ensemble_quant_loss import (
|
||||
EnsembleQuantLoss,
|
||||
AsymmetricQuantLoss,
|
||||
)
|
||||
from src.training.registry import register_model
|
||||
|
||||
|
||||
@@ -235,8 +238,8 @@ class TabMModel(BaseModel):
|
||||
optimizer, T_max=epochs, eta_min=1e-6
|
||||
)
|
||||
|
||||
# 使用 EnsembleQuantLoss 替代 MSE
|
||||
self.criterion = EnsembleQuantLoss(
|
||||
# 使用 AsymmetricQuantLoss (非对称 Huber + IC)
|
||||
self.criterion = AsymmetricQuantLoss(
|
||||
alpha=self.params.get("loss_alpha", 0.5), ensemble_size=ensemble_size
|
||||
)
|
||||
|
||||
|
||||
Reference in New Issue
Block a user