市场趋势和波动率的状态变化¶
- 通过分析市场趋势和波动率的状态变化规律,判断市场是否具有平稳性。如果状态变化间隔呈现随机分布,说明市场较平稳;如果长期停留在某一状态(如长期高波动率),则可能存在非平稳性,会影响交易系统的 robustness。通过分析市场指标的状态变化规律,检验市场平稳性,为交易系统的开发提供依据(避免在非平稳市场中失效)。
-
关键指标计算:
-
趋势指标:使用最小二乘拟合斜率(
find_slope)衡量价格趋势 -
波动率指标:使用平均真实波动幅度(ATR,
find_volatility)衡量市场波动 -
指标版本处理:
-
版本 0:使用原始指标值
- 版本 1:当前指标值减去 "lookback" 天前的指标值(振荡器形式)
-
版本 > 1:当前指标值减去 "version×lookback" 天前的指标值
-
状态变化分析:
-
通过分位数(
fractile参数)将指标分为 "高于阈值" 和 "低于阈值" 两种状态 - 记录状态变化的间隔天数(
gap_analyze函数) -
将间隔天数分为 11 个区间(1,2,4,8,16,32,64,128,256,512,>512),统计每个区间的出现次数
-
平稳性判断依据:
-
如果各区间的出现次数分布较均匀,说明状态变化随机,市场较平稳
- 如果大区间(如 > 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;
}
通过 “指标差值法” 提升市场指标的平稳性,主要有两种实现方式:一是当前指标减滞后指标,二是短期指标减长期指标。
- 两种指标修改方法的实现 方法一(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) 实现,通过长期窗口平滑极端波动,同时保留短期变化信息。
- 平稳性的量化验证 状态变化分析:通过 analyze_state_gaps 函数统计指标在 “高于中位数” 和 “低于中位数” 两种状态间的切换间隔,间隔越随机(各区间分布均匀),平稳性越好。 结果对比: 原始指标(Version=0)存在大量长期状态(如波动率的 > 512 区间次数),而修改后的指标(Version=1/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;
}