跳转至

市场趋势和波动率的状态变化

  1. 通过分析市场趋势和波动率的状态变化规律,判断市场是否具有平稳性。如果状态变化间隔呈现随机分布,说明市场较平稳;如果长期停留在某一状态(如长期高波动率),则可能存在非平稳性,会影响交易系统的 robustness。通过分析市场指标的状态变化规律,检验市场平稳性,为交易系统的开发提供依据(避免在非平稳市场中失效)。
  2. 关键指标计算:

  3. 趋势指标:使用最小二乘拟合斜率(find_slope)衡量价格趋势

  4. 波动率指标:使用平均真实波动幅度(ATR,find_volatility)衡量市场波动

  5. 指标版本处理:

  6. 版本 0:使用原始指标值

  7. 版本 1:当前指标值减去 "lookback" 天前的指标值(振荡器形式)
  8. 版本 > 1:当前指标值减去 "version×lookback" 天前的指标值

  9. 状态变化分析:

  10. 通过分位数(fractile参数)将指标分为 "高于阈值" 和 "低于阈值" 两种状态

  11. 记录状态变化的间隔天数(gap_analyze函数)
  12. 将间隔天数分为 11 个区间(1,2,4,8,16,32,64,128,256,512,>512),统计每个区间的出现次数

  13. 平稳性判断依据:

  14. 如果各区间的出现次数分布较均匀,说明状态变化随机,市场较平稳

  15. 如果大区间(如 > 512)出现次数过多,说明市场长期停留在某一状态,存在非平稳性风险
#include <algorithm>
#include <climits>
#include <cmath>
#include <cstdlib>
#include <fstream>
#include <iostream>
#include <vector>

using namespace std;

// 统一常量:状态变化区间数量
const int NGAPS = 11;

// 1. 计算趋势指标(最小二乘斜率)
double calc_trend(const vector<double>& close, int start_idx, int lookback) {
  double sum_x = 0.0, sum_y = 0.0, sum_xy = 0.0, sum_x2 = 0.0;
  for (int i = 0; i < lookback; ++i) {
    double x = i;
    double y = close[start_idx - i];  // 从start_idx向前取lookback个数据
    sum_x += x;
    sum_y += y;
    sum_xy += x * y;
    sum_x2 += x * x;
  }
  double denominator = lookback * sum_x2 - sum_x * sum_x;
  return denominator == 0 ? 0.0
                          : (lookback * sum_xy - sum_x * sum_y) / denominator;
}

// 2. 计算波动率指标(ATR)
double calc_volatility(const vector<double>& high, const vector<double>& low,
                       const vector<double>& close, int start_idx,
                       int lookback) {
  double sum_tr = 0.0;
  for (int i = 0; i < lookback; ++i) {
    int idx = start_idx - i;
    double tr1 = high[idx] - low[idx];
    double tr2 = fabs(
        high[idx] - close[idx + (i == lookback - 1 ? 0 : 1)]);  // 前一日收盘价
    double tr3 = fabs(low[idx] - close[idx + (i == lookback - 1 ? 0 : 1)]);
    sum_tr += max({tr1, tr2, tr3});  // 取真实波动最大值
  }
  return sum_tr / lookback;  // 平均真实波动
}

// 3. 分析状态变化间隔分布
void analyze_state_gaps(const vector<double>& indicator, double quantile,
                        vector<int>& gap_counts,
                        const vector<int>& gap_bounds) {
  gap_counts.assign(NGAPS, 0);
  if (indicator.empty()) return;

  int current_state = (indicator[0] >= quantile) ? 1 : 0;
  int state_duration = 1;

  // 遍历指标,统计状态变化
  for (size_t i = 1; i < indicator.size(); ++i) {
    int new_state = (indicator[i] >= quantile) ? 1 : 0;
    if (new_state == current_state) {
      state_duration++;
    } else {
      // 匹配当前持续时间所属区间
      for (int j = 0; j < NGAPS; ++j) {
        if (state_duration <= gap_bounds[j]) {
          gap_counts[j]++;
          break;
        }
      }
      current_state = new_state;
      state_duration = 1;
    }
  }
  // 统计最后一段状态持续时间
  for (int j = 0; j < NGAPS; ++j) {
    if (state_duration <= gap_bounds[j]) {
      gap_counts[j]++;
      break;
    }
  }
}

// 4. 读取市场数据文件(格式:YYYYMMDD Open High Low Close)
bool read_market_data(const string& filename, vector<int>& dates,
                      vector<double>& open, vector<double>& high,
                      vector<double>& low, vector<double>& close) {
  ifstream file(filename);
  if (!file.is_open()) {
    cerr << "无法打开文件: " << filename << endl;
    return false;
  }

  int date;
  double o, h, l, c;
  while (file >> date >> o >> h >> l >> c) {
    dates.push_back(date);
    open.push_back(o);
    high.push_back(h);
    low.push_back(l);
    close.push_back(c);
  }
  file.close();
  return !close.empty();
}

int main(int argc, char* argv[]) {
  // 解析命令行参数(STATN Lookback Fractile Version Filename)
  if (argc != 5) {
    cerr << "用法: STATN <Lookback> <Fractile> <Version> <Filename>" << endl;
    cerr << "示例: STATN 100 0.5 1 SPX_2020_2024.txt" << endl;
    return 1;
  }

  // 解析参数
  int lookback = atoi(argv[1]);
  double fractile = atof(argv[2]);
  int version = atoi(argv[3]);
  string filename = argv[4];

  // 校验参数合法性
  if (lookback < 1 || fractile < 0 || fractile > 1 || version < 0) {
    cerr << "参数错误:" << endl;
    cerr << "- Lookback需≥1,Fractile需在[0,1],Version需≥0" << endl;
    return 1;
  }

  // 读取市场数据
  vector<int> dates;
  vector<double> open, high, low, close;
  if (!read_market_data(filename, dates, open, high, low, close)) {
    cerr << "数据读取失败" << endl;
    return 1;
  }
  int nprices = close.size();
  if (nprices < lookback) {
    cerr << "数据不足(需≥" << lookback << "根K线,实际:" << nprices << ")"
         << endl;
    return 1;
  }

  // 计算完整回溯窗口(统一逻辑)
  int full_lookback = (version == 0)   ? lookback
                      : (version == 1) ? 2 * lookback
                                       : version * lookback;
  int nind = nprices - full_lookback + 1;  // 指标数量
  if (nind <= 0) {
    cerr << "完整回溯窗口过大(" << full_lookback << "),无法计算指标" << endl;
    return 1;
  }

  // 计算趋势指标(单版本)
  vector<double> trend(nind);
  for (int i = 0; i < nind; ++i) {
    int k = full_lookback - 1 + i;  // 当前计算位置
    double trend_short = calc_trend(close, k, lookback);
    if (version == 0) {
      trend[i] = trend_short;
    } else if (version == 1) {
      double trend_lag = calc_trend(close, k - lookback, lookback);
      trend[i] = trend_short - trend_lag;
    } else {
      double trend_long = calc_trend(close, k, full_lookback);
      trend[i] = trend_short - trend_long;
    }
  }

  // 计算波动率指标(单版本)
  vector<double> volatility(nind);
  for (int i = 0; i < nind; ++i) {
    int k = full_lookback - 1 + i;
    double vol_short = calc_volatility(high, low, close, k, lookback);
    if (version == 0) {
      volatility[i] = vol_short;
    } else if (version == 1) {
      double vol_lag =
          calc_volatility(high, low, close, k - lookback, lookback);
      volatility[i] = vol_short - vol_lag;
    } else {
      double vol_long = calc_volatility(high, low, close, k, full_lookback);
      volatility[i] = vol_short - vol_long;
    }
  }

  // 计算分位数(统一逻辑)
  vector<double> trend_sorted = trend;
  vector<double> vol_sorted = volatility;
  sort(trend_sorted.begin(), trend_sorted.end());
  sort(vol_sorted.begin(), vol_sorted.end());

  int trend_q_idx = min(max(0, (int)(fractile * nind)), nind - 1);
  int vol_q_idx = min(max(0, (int)(fractile * nind)), nind - 1);
  double trend_quantile = trend_sorted[trend_q_idx];
  double vol_quantile = vol_sorted[vol_q_idx];

  // 定义状态间隔区间(统一)
  vector<int> gap_bounds = {1, 2, 4, 8, 16, 32, 64, 128, 256, 512, INT_MAX};
  vector<int> trend_gap_counts, vol_gap_counts;

  // 分析状态变化
  analyze_state_gaps(trend, trend_quantile, trend_gap_counts, gap_bounds);
  analyze_state_gaps(volatility, vol_quantile, vol_gap_counts, gap_bounds);

  // 输出结果(格式化)
  cout << "=== STATN 分析结果(Lookback=" << lookback
       << ", Fractile=" << fractile << ", Version=" << version
       << ")===" << endl;
  cout << "\n1. 趋势指标状态变化分布(分位数:" << fixed << trend_quantile
       << ")" << endl;
  cout << "区间(天数) | 出现次数" << endl;
  cout << "-----------|--------" << endl;
  for (int i = 0; i < NGAPS; ++i) {
    if (i < NGAPS - 1) {
      cout << "1-" << gap_bounds[i] << " | " << trend_gap_counts[i] << endl;
    } else {
      cout << ">" << gap_bounds[i - 1] << " | " << trend_gap_counts[i] << endl;
    }
  }

  cout << "\n2. 波动率指标状态变化分布(分位数:" << fixed << vol_quantile
       << ")" << endl;
  cout << "区间(天数) | 出现次数" << endl;
  cout << "-----------|--------" << endl;
  for (int i = 0; i < NGAPS; ++i) {
    if (i < NGAPS - 1) {
      cout << "1-" << gap_bounds[i] << " | " << vol_gap_counts[i] << endl;
    } else {
      cout << ">" << gap_bounds[i - 1] << " | " << vol_gap_counts[i] << endl;
    }
  }

  return 0;
}

通过 “指标差值法” 提升市场指标的平稳性,主要有两种实现方式:一是当前指标减滞后指标,二是短期指标减长期指标。

  1. 两种指标修改方法的实现 方法一(Version=1):当前指标 - 滞后指标对应文中 “计算指标值并减去滞后值,滞后长度通常等于指标回溯窗口”。代码中用 calc_trend(close, i, LOOKBACK) - calc_trend(close, i - LOOKBACK, LOOKBACK) 实现,直接消除指标的长期趋势偏移。 方法二(Version=3):短期指标 - 长期指标对应文中 “用不同回溯窗口计算指标,短期减长期得到更平稳的修改指标”。代码中用 calc_trend(close, i, LOOKBACK) - calc_trend(close, i, 3*LOOKBACK) 实现,通过长期窗口平滑极端波动,同时保留短期变化信息。
  2. 平稳性的量化验证 状态变化分析:通过 analyze_state_gaps 函数统计指标在 “高于中位数” 和 “低于中位数” 两种状态间的切换间隔,间隔越随机(各区间分布均匀),平稳性越好。 结果对比: 原始指标(Version=0)存在大量长期状态(如波动率的 > 512 区间次数),而修改后的指标(Version=1/3)长期状态次数显著减少,平稳性明显提升。
  3. 方法间的权衡体现 Version=1:平稳性最优,但丢失原始指标的绝对值信息(如当前趋势的具体大小),对应文中 “最强大但丢弃信息最多”。 Version=3:通过调整长期窗口(如 3*LOOKBACK),在平稳性和信息保留间取得平衡,对应文中 “折中方案,可通过调整长期回溯窗口控制权衡关系”。
#include <algorithm>
#include <climits>
#include <cmath>
#include <iomanip>
#include <iostream>
#include <random>
#include <vector>

using namespace std;

// 统一常量:状态变化区间数量
const int NGAPS = 11;

// 1. 计算趋势指标
double calc_trend(const vector<double>& close, int start_idx, int lookback) {
  double sum_x = 0.0, sum_y = 0.0, sum_xy = 0.0, sum_x2 = 0.0;
  for (int i = 0; i < lookback; ++i) {
    double x = i;
    double y = close[start_idx - i];
    sum_x += x;
    sum_y += y;
    sum_xy += x * y;
    sum_x2 += x * x;
  }
  double denominator = lookback * sum_x2 - sum_x * sum_x;
  return denominator == 0 ? 0.0
                          : (lookback * sum_xy - sum_x * sum_y) / denominator;
}

// 2. 计算波动率指标
double calc_volatility(const vector<double>& high, const vector<double>& low,
                       const vector<double>& close, int start_idx,
                       int lookback) {
  double sum_tr = 0.0;
  for (int i = 0; i < lookback; ++i) {
    int idx = start_idx - i;
    double tr1 = high[idx] - low[idx];
    double tr2 = fabs(high[idx] - close[idx + (i == lookback - 1 ? 0 : 1)]);
    double tr3 = fabs(low[idx] - close[idx + (i == lookback - 1 ? 0 : 1)]);
    sum_tr += max({tr1, tr2, tr3});
  }
  return sum_tr / lookback;
}

// 3. 分析状态变化间隔分布
void analyze_state_gaps(const vector<double>& indicator, double quantile,
                        vector<int>& gap_counts,
                        const vector<int>& gap_bounds) {
  gap_counts.assign(NGAPS, 0);
  if (indicator.empty()) return;

  int current_state = (indicator[0] >= quantile) ? 1 : 0;
  int state_duration = 1;

  for (size_t i = 1; i < indicator.size(); ++i) {
    int new_state = (indicator[i] >= quantile) ? 1 : 0;
    if (new_state == current_state) {
      state_duration++;
    } else {
      for (int j = 0; j < NGAPS; ++j) {
        if (state_duration <= gap_bounds[j]) {
          gap_counts[j]++;
          break;
        }
      }
      current_state = new_state;
      state_duration = 1;
    }
  }
  // 统计最后一段
  for (int j = 0; j < NGAPS; ++j) {
    if (state_duration <= gap_bounds[j]) {
      gap_counts[j]++;
      break;
    }
  }
}

// 4. 生成模拟S&P 100指数数据
void generate_simulated_oex_data(int bar_count, vector<double>& high,
                                 vector<double>& low, vector<double>& close) {
  random_device rd;
  mt19937 gen(rd());
  normal_distribution<double> trend_dist(0.01, 0.005);  // 趋势漂移
  normal_distribution<double> vol_dist(1.0, 0.3);       // 波动幅度
  normal_distribution<double> open_dist(0, 0.5);        // 开盘价偏差

  close.push_back(1000.0);  // 初始收盘价
  high.push_back(1001.0);   // 初始最高价
  low.push_back(999.0);     // 初始最低价

  for (int i = 1; i < bar_count; ++i) {
    double prev_close = close.back();
    double trend = trend_dist(gen);
    double vol = vol_dist(gen);

    // 计算当日价格
    double open = prev_close + vol * open_dist(gen);
    double day_high =
        open + vol * (0.5 + 0.2 * normal_distribution<double>(0, 1)(gen));
    double day_low =
        open - vol * (0.5 + 0.2 * normal_distribution<double>(0, 1)(gen));
    double day_close = prev_close + trend * vol;

    // 确保高低价合理
    day_high = max(day_high, day_close);
    day_low = min(day_low, day_close);

    high.push_back(day_high);
    low.push_back(day_low);
    close.push_back(day_close);
  }
}

int main() {
  // 统一配置参数
  const int LOOKBACK = 100;          // 基础回溯窗口
  const double FRACTILE = 0.5;       // 分位数(中位数)
  const int TOTAL_BARS = 2000;       // 模拟K线数量
  const int VERSIONS[] = {0, 1, 3};  // 对比的3个版本
  const int N_VERSIONS = sizeof(VERSIONS) / sizeof(VERSIONS[0]);

  // 生成模拟数据
  vector<double> high, low, close;
  generate_simulated_oex_data(TOTAL_BARS, high, low, close);
  cout << "=== 模拟S&P 100数据生成完成 ===" << endl;
  cout << "K线数量:" << TOTAL_BARS << " | 基础回溯窗口:" << LOOKBACK
       << " | 分位数:" << FRACTILE << endl
       << endl;

  // 计算3个版本的趋势和波动率指标
  vector<vector<double>> trend_versions(N_VERSIONS), vol_versions(N_VERSIONS);
  for (int v_idx = 0; v_idx < N_VERSIONS; ++v_idx) {
    int version = VERSIONS[v_idx];
    // 统一计算完整回溯窗口
    int full_lookback = (version == 0)   ? LOOKBACK
                        : (version == 1) ? 2 * LOOKBACK
                                         : version * LOOKBACK;
    int start_idx = full_lookback - 1;  // 第一个可计算指标的位置
    int nind = close.size() - full_lookback + 1;

    if (nind <= 0) {
      cerr << "版本" << version << ":完整回溯窗口过大,无法计算指标" << endl;
      continue;
    }

    // 计算趋势指标
    vector<double> trend(nind);
    for (int i = 0; i < nind; ++i) {
      int k = start_idx + i;
      double trend_short = calc_trend(close, k, LOOKBACK);
      if (version == 0) {
        trend[i] = trend_short;
      } else if (version == 1) {
        double trend_lag = calc_trend(close, k - LOOKBACK, LOOKBACK);
        trend[i] = trend_short - trend_lag;
      } else {
        double trend_long = calc_trend(close, k, full_lookback);
        trend[i] = trend_short - trend_long;
      }
    }
    trend_versions[v_idx] = trend;

    // 计算波动率指标
    vector<double> volatility(nind);
    for (int i = 0; i < nind; ++i) {
      int k = start_idx + i;
      double vol_short = calc_volatility(high, low, close, k, LOOKBACK);
      if (version == 0) {
        volatility[i] = vol_short;
      } else if (version == 1) {
        double vol_lag =
            calc_volatility(high, low, close, k - LOOKBACK, LOOKBACK);
        volatility[i] = vol_short - vol_lag;
      } else {
        double vol_long = calc_volatility(high, low, close, k, full_lookback);
        volatility[i] = vol_short - vol_long;
      }
    }
    vol_versions[v_idx] = volatility;
  }

  // 计算各版本指标的分位数(统一逻辑)
  vector<double> trend_quantiles(N_VERSIONS), vol_quantiles(N_VERSIONS);
  for (int v_idx = 0; v_idx < N_VERSIONS; ++v_idx) {
    vector<double> trend_sorted = trend_versions[v_idx];
    vector<double> vol_sorted = vol_versions[v_idx];
    sort(trend_sorted.begin(), trend_sorted.end());
    sort(vol_sorted.begin(), vol_sorted.end());

    int nind = trend_sorted.size();
    int q_idx = min(max(0, (int)(FRACTILE * nind)), nind - 1);
    trend_quantiles[v_idx] = trend_sorted[q_idx];
    vol_quantiles[v_idx] = vol_sorted[q_idx];
  }

  // 分析各版本状态变化(统一区间)
  vector<int> gap_bounds = {1, 2, 4, 8, 16, 32, 64, 128, 256, 512, INT_MAX};
  vector<vector<int>> trend_gap_counts(N_VERSIONS), vol_gap_counts(N_VERSIONS);
  for (int v_idx = 0; v_idx < N_VERSIONS; ++v_idx) {
    analyze_state_gaps(trend_versions[v_idx], trend_quantiles[v_idx],
                       trend_gap_counts[v_idx], gap_bounds);
    analyze_state_gaps(vol_versions[v_idx], vol_quantiles[v_idx],
                       vol_gap_counts[v_idx], gap_bounds);
  }

  // 格式化输出结果
  cout << "=== 趋势指标状态变化分布对比(分位数:" << fixed << setprecision(6)
       << trend_quantiles[0] << ")===" << endl;
  cout << setw(12) << "区间(天数)";
  for (int v_idx = 0; v_idx < N_VERSIONS; ++v_idx) {
    cout << setw(12) << "Version=" << VERSIONS[v_idx];
  }
  cout << endl;
  cout << string(12 + 12 * N_VERSIONS, '-') << endl;

  for (int i = 0; i < NGAPS; ++i) {
    if (i < NGAPS - 1) {
      cout << setw(12) << ("1-" + to_string(gap_bounds[i]));
    } else {
      cout << setw(12) << (">" + to_string(gap_bounds[i - 1]));
    }
    for (int v_idx = 0; v_idx < N_VERSIONS; ++v_idx) {
      cout << setw(12) << trend_gap_counts[v_idx][i];
    }
    cout << endl;
  }

  cout << "\n=== 波动率指标状态变化分布对比(分位数:" << fixed
       << setprecision(2) << vol_quantiles[0] << ")===" << endl;
  cout << setw(12) << "区间(天数)";
  for (int v_idx = 0; v_idx < N_VERSIONS; ++v_idx) {
    cout << setw(12) << "Version=" << VERSIONS[v_idx];
  }
  cout << endl;
  cout << string(12 + 12 * N_VERSIONS, '-') << endl;

  for (int i = 0; i < NGAPS; ++i) {
    if (i < NGAPS - 1) {
      cout << setw(12) << ("1-" + to_string(gap_bounds[i]));
    } else {
      cout << setw(12) << (">" + to_string(gap_bounds[i - 1]));
    }
    for (int v_idx = 0; v_idx < N_VERSIONS; ++v_idx) {
      cout << setw(12) << vol_gap_counts[v_idx][i];
    }
    cout << endl;
  }

  // 输出结论
  cout << "\n=== 关键结论 ===" << endl;
  cout << "1. "
          "Version=1(当前-滞后):长期状态(>"
          "512天)次数最少,平稳性最优,但丢失原始信息最多。"
       << endl;
  cout << "2. "
          "Version=3(短期-长期):平稳性优于原始指标(Version="
          "0),且保留更多信息,为折中方案。"
       << endl;
  cout << "3. 波动率指标非平稳性更显著,修改后提升效果比趋势指标更明显。"
       << endl;

  return 0;
}