交易指标评估的信息熵¶
1. 用“熵”量化指标信息价值
- 交易指标(如趋势、波动率)可视为市场传递的“信息”,其价值取决于消除市场未来状态不确定性的能力,这一能力用“熵”衡量。
- 计算指标相对熵(实际熵值/最大可能熵值),将信息含量标准化到0-1范围:0表示无信息,1表示信息最大化,以此作为指标价值的核心评判标准,通常要求相对熵至少0.5。
- 分箱是计算熵的关键操作:需将指标数值范围按等间隔分成多个区间(如20个),统计每个区间的指标值占比,再代入香农熵公式(H = -Σp(x)·log(p(x)))计算,避免等数量分箱导致的熵值失真。
2. 低熵指标的问题与优化方法
- 低熵的危害:低熵指标(如含极端异常值、数值过度聚类)会干扰模型训练,例如异常值会让模型过度关注少数数据,忽略多数正常样本;数值聚类会导致模型误将无关聚类差异当作核心规律,降低预测准确性。
- 优化手段:
- 尾部清洗:通过排序找到指标的核心数据区间(覆盖大部分正常数据),对区间外的极端值进行单调压缩(如指数函数压缩),既保留数据顺序,又避免异常值破坏分布,提升熵值。
- 非线性变换:针对不同分布缺陷选择对应变换,如平方根/立方根处理偏态分布、双曲正切/逻辑函数压缩极端值、累积分布函数匹配标准分布,优化指标数值分布以提高信息含量。
3. 熵与交易模型的关联
- 高熵指标通常对应数值在范围内均匀分布,这种分布能让预测模型更高效学习数据规律,减少异常值或聚类带来的干扰,因此熵值高低与模型训练效果高度相关。
- 需注意熵的局限性:高熵仅代表指标信息含量高,需进一步验证信息是否与交易目标(如判断多空、预测波动率)相关;少数情况下优化熵可能降低指标实用性,但属于例外情况,多数时候熵提升会改善模型性能。
#include <algorithm>
#include <cmath>
#include <cstdint>
#include <cstring>
#include <vector>
// 错误码定义
enum EntropyCalcErr {
ERR_SUCCESS = 0,
ERR_EMPTY_DATA = 1,
ERR_INVALID_BINS = 2,
ERR_INVALID_TAIL_FRAC = 3,
ERR_NULL_PTR = 4
};
// 熵计算与指标优化工具类
class TradingIndicatorEntropy {
public:
// 1. 计算指标的相对熵(核心分箱算法)
// 参数:数据长度n、数据指针x、分箱数nbins、结果输出指针rel_entropy
static int CalculateRelativeEntropy(int n, const double* x, int nbins,
double* rel_entropy) {
// 输入合法性检查
if (x == nullptr || rel_entropy == nullptr) return ERR_NULL_PTR;
if (n <= 0) return ERR_EMPTY_DATA;
if (nbins < 2) return ERR_INVALID_BINS;
// 步骤1:计算数据的最小值和最大值
double min_val = x[0], max_val = x[0];
for (int i = 1; i < n; ++i) {
if (x[i] < min_val) min_val = x[i];
if (x[i] > max_val) max_val = x[i];
}
// 步骤2:计算分箱映射因子(避免除以零和越界)
const double denom = (max_val - min_val) + 1e-60; // 防止所有数据相等
const double factor = (nbins - 1e-10) / denom; // 防止映射到不存在的分箱
// 步骤3:统计每个分箱的计数
int* bin_counts = new int[nbins](); // 初始化全0
for (int i = 0; i < n; ++i) {
const int bin_idx = static_cast<int>(factor * (x[i] - min_val));
bin_counts[bin_idx]++;
}
// 步骤4:计算香农熵并归一化为相对熵
double entropy = 0.0;
const double n_inv = 1.0 / static_cast<double>(n);
const double log_nbins = log(static_cast<double>(nbins));
for (int i = 0; i < nbins; ++i) {
if (bin_counts[i] == 0) continue; // 0*log(0)定义为0,跳过
const double p = static_cast<double>(bin_counts[i]) * n_inv;
entropy -= p * log(p);
}
// 计算相对熵(H(X)/log(K))
*rel_entropy = entropy / log_nbins;
// 释放内存
delete[] bin_counts;
return ERR_SUCCESS;
}
// 2. 单调尾部清洗(仅处理极端值)
// 参数:数据长度n、原始数据指针raw(会被修改)、工作区指针work(需提前分配n个元素空间)
static int CleanTails(int n, double* raw, double* work, double tail_frac) {
// 输入合法性检查
if (raw == nullptr || work == nullptr) return ERR_NULL_PTR;
if (n <= 0) return ERR_EMPTY_DATA;
if (tail_frac <= 0.0 || tail_frac >= 0.5) return ERR_INVALID_TAIL_FRAC;
// 步骤1:复制数据到工作区并排序
memcpy(work, raw, sizeof(double) * n);
std::sort(work, work + n);
// 步骤2:确定核心数据区间(覆盖cover比例的数据,找范围最小的区间)
const double cover = 1.0 - 2.0 * tail_frac;
int istop = static_cast<int>(cover * (n + 1)) - 1;
istop = (istop >= n) ? (n - 1) : istop;
int istart = 0;
double min_range = 1e60;
int best_start = 0, best_stop = 0;
while (istop < n) {
const double range = work[istop] - work[istart];
if (range < min_range) {
min_range = range;
best_start = istart;
best_stop = istop;
}
istart++;
istop++;
}
// 步骤3:获取核心区间的边界(避免极端情况)
double min_core = work[best_start];
double max_core = work[best_stop];
if (max_core <= min_core) {
max_core *= 1.0 + 1e-10;
min_core *= 1.0 - 1e-10;
}
// 步骤4:压缩尾部数据(单调变换,保留顺序)
const double core_range = max_core - min_core;
const double limit = core_range * (1.0 - cover);
const double scale = -1.0 / core_range;
for (int i = 0; i < n; ++i) {
if (raw[i] < min_core) {
// 左尾部压缩
raw[i] = min_core - limit * (1.0 - exp(scale * (min_core - raw[i])));
} else if (raw[i] > max_core) {
// 右尾部压缩
raw[i] = max_core + limit * (1.0 - exp(scale * (raw[i] - max_core)));
}
// 核心区间数据保持不变
}
return ERR_SUCCESS;
}
// 3. 指标非线性变换(提升熵值)
// 参数:数据长度n、输入数据、变换类型(1:平方根 2:立方根 3:双曲正切
// 4:逻辑函数)、输出数据
static int ApplyNonlinearTransform(int n, const double* in_data,
int transform_type, double* out_data) {
if (in_data == nullptr || out_data == nullptr) return ERR_NULL_PTR;
if (n <= 0) return ERR_EMPTY_DATA;
// 预分配输出内存(若out_data未提前分配,此处需处理,此处假设已分配)
for (int i = 0; i < n; ++i) {
const double val = in_data[i];
switch (transform_type) {
case 1: // 平方根(处理非负,适合右偏数据)
out_data[i] = (val < 0.0) ? 0.0 : sqrt(val);
break;
case 2: // 立方根(处理双尾极端值)
out_data[i] = cbrt(val);
break;
case 3: // 双曲正切(压缩极端值,需提前归一化)
out_data[i] = tanh(val);
break;
case 4: // 逻辑函数(压缩后居中到-0.5~0.5)
out_data[i] = 1.0 / (1.0 + exp(-val)) - 0.5;
break;
default:
out_data[i] = val; // 默认无变换
break;
}
}
return ERR_SUCCESS;
}
};
// 示例:交易系统中调用流程
int main() {
// 假设从市场数据中读取的指标数据(如RawJump)
const int data_len = 10000;
double* raw_indicator = new double[data_len];
double* work_buf = new double[data_len]; // 尾部清洗工作区
double* transformed_indicator = new double[data_len]; // 变换后数据
// 1. 计算原始指标的相对熵
double raw_rel_entropy;
int err = TradingIndicatorEntropy::CalculateRelativeEntropy(
data_len, raw_indicator, 20, &raw_rel_entropy);
if (err != ERR_SUCCESS) {
// 实际系统中记录错误日志,此处简化
goto cleanup;
}
// 2. 若熵值过低(如<0.5),先进行尾部清洗
if (raw_rel_entropy < 0.5) {
err = TradingIndicatorEntropy::CleanTails(data_len, raw_indicator, work_buf,
0.05); // 清洗5%尾部
if (err != ERR_SUCCESS) goto cleanup;
// 计算清洗后的熵值
double cleaned_rel_entropy;
err = TradingIndicatorEntropy::CalculateRelativeEntropy(
data_len, raw_indicator, 20, &cleaned_rel_entropy);
if (err != ERR_SUCCESS) goto cleanup;
}
// 3. 若熵值仍不足,应用非线性变换(如双曲正切)
err = TradingIndicatorEntropy::ApplyNonlinearTransform(
data_len, raw_indicator, 3, transformed_indicator);
if (err != ERR_SUCCESS) goto cleanup;
// 4. 计算最终优化后指标的相对熵(供交易模型使用)
double final_rel_entropy;
err = TradingIndicatorEntropy::CalculateRelativeEntropy(
data_len, transformed_indicator, 20, &final_rel_entropy);
cleanup:
// 释放内存(避免内存泄漏)
delete[] raw_indicator;
delete[] work_buf;
delete[] transformed_indicator;
return err;
}