在量化交易领域摸爬滚打多年,我发现一个有趣的现象:大多数交易者仍在使用几十年前的技术指标,却期望在瞬息万变的市场中获得超额收益。这就像用算盘去解决微积分问题——工具本身没有问题,但效率和精度已经跟不上时代了。
今天要分析的这个高级KNN(K-近邻)交易策略,恰恰代表了量化交易的一个重要发展方向:将机器学习算法与传统技术分析相结合,构建更智能的交易决策系统。
KNN算法的核心思想极其简单却又深刻:相似的市场环境往往产生相似的价格走势。这个假设在金融市场中有着坚实的理论基础——市场参与者的行为模式具有一定的重复性和可预测性。
该策略的特色在于构建了一个七维特征空间: - 价格动量:衡量价格变化的速度和方向 - RSI指标:反映超买超卖状态 - 成交量比率:揭示资金流向变化 - 波动率:量化市场情绪波动 - 趋势强度:通过双均线系统识别趋势 - MACD特征:捕捉动量转换信号 - 布林带位置:判断价格相对位置
这里有一个关键的技术细节值得深入探讨:特征标准化。策略使用Z-score标准化方法,将所有特征转换到相同的数值范围内。这一步骤至关重要,因为:
normalize(src, length) =>
mean_val = ta.sma(src, length)
std_val = ta.stdev(src, length)
std_val == 0 ? 0.0 : (src - mean_val) / std_val
传统的KNN算法通常采用简单投票机制,但这个策略采用了更精妙的距离加权方法。距离越近的历史样本,其预测权重越高。这种设计反映了一个重要的金融市场特征:市场状态的相似度是连续的,而非离散的。
权重计算公式:weight = 1.0 / (distance + 0.001)
这种加权机制能够: - 更精确地反映历史相似度 - 减少噪声数据的干扰 - 提高预测结果的可靠性
基于我对机器学习交易策略的研究经验,KNN策略在以下市场环境中通常表现较好:
需要注意的是,该策略设置了较为保守的风险管理参数:2%止损,4%止盈,这种1:2的风险收益比体现了策略设计者对风险控制的重视。
这个策略的几个创新之处值得称赞:
但同时,我也看到了一些改进空间: - 特征选择优化:可以引入特征重要性评估,动态调整特征权重 - 参数自适应:K值和阈值可以根据市场状态动态调整 - 多时间框架融合:结合不同周期的信号可能提高预测准确性
在实盘应用中,需要特别关注以下几点:
这个KNN策略代表了量化交易发展的一个重要方向:从简单的规则驱动转向智能的数据驱动。虽然机器学习不是万能的,但它为我们提供了一种更科学、更系统的方法来理解和预测市场行为。
在我看来,未来的量化交易将是传统金融理论、现代统计学和机器学习技术的深度融合。这个KNN策略只是一个开始,更多的创新和突破还在路上。
/*backtest
start: 2024-09-04 09:00:00
end: 2025-09-03 15:00:00
period: 1h
basePeriod: 1h
exchanges: [{"eid":"Futures_CTP","currency":"FUTURES"}]
*/
//@version=6
strategy("Advanced KNN Trading Strategy", overlay=true, max_bars_back=500)
//输入参数配置:设置KNN算法和交易策略的各项参数
k = input.int(5, "KNN邻居数量", minval=3, maxval=50);//K最近邻算法中K值
lookback = input.int(40, "历史数据回望期", minval=50, maxval=500);//历史数据窗口大小
feature_length = input.int(5, "特征计算周期", minval=5, maxval=20);//技术指标计算周期
norm_length = input.int(5, "标准化计算周期", minval=20, maxval=100);//特征标准化周期
prediction_threshold = input.float(0.8, "预测阈值", minval=0.5, maxval=0.9, step=0.1);//交易信号阈值
stop_loss_pct = input.float(2.0, "止损百分比", minval=0.5, maxval=5.0, step=0.1);//止损比例
take_profit_pct = input.float(4.0, "止盈百分比", minval=1.0, maxval=10.0, step=0.1);//止盈比例
//特征工程函数:数据预处理和特征提取
//标准化函数:将原始数据转换为标准正态分布
normalize(src, length) =>
mean_val = ta.sma(src, length);//计算均值
std_val = ta.stdev(src, length);//计算标准差
std_val == 0 ? 0.0 : (src - mean_val) / std_val;//Z-score标准化
//原始技术特征计算:提取价格、成交量、波动率等基础特征
raw_price_momentum = (close - close[feature_length]) / close[feature_length] * 100;//价格动量
raw_rsi = ta.rsi(close, feature_length);//相对强弱指数
raw_volume_ratio = volume / ta.sma(volume, feature_length);//成交量比率
raw_volatility = ta.stdev(ta.change(close), feature_length) / close * 100;//价格波动率
//趋势特征计算:识别价格趋势强度
sma_short = ta.sma(close, 5);//短期均线
sma_long = ta.sma(close, 20);//长期均线
raw_trend_strength = (sma_short - sma_long) / sma_long * 100;//趋势强度
//MACD特征计算:动量指标
[macd_line, signal_line, histogram] = ta.macd(close, 12, 26, 9);//MACD指标
raw_macd_feature = macd_line - signal_line;//MACD差值
//布林带特征计算:价格相对位置
[bb_middle, bb_upper, bb_lower] = ta.bb(close, 20, 2);//布林带
raw_bb_position = (close - bb_lower) / (bb_upper - bb_lower);//价格在布林带中的位置
//特征标准化:将所有特征转换为相同量级
price_momentum = normalize(raw_price_momentum, norm_length);//标准化价格动量
rsi_feature = normalize(raw_rsi, norm_length);//标准化RSI
volume_ratio = normalize(raw_volume_ratio, norm_length);//标准化成交量比率
volatility = normalize(raw_volatility, norm_length);//标准化波动率
trend_strength = normalize(raw_trend_strength, norm_length);//标准化趋势强度
macd_feature = normalize(raw_macd_feature, norm_length);//标准化MACD
bb_position = normalize(raw_bb_position, norm_length);//标准化布林带位置
//目标变量定义:预测标签生成
future_return = (close - close[1]) / close[1] * 100;//未来收益率
target_label = future_return > 0 ? 1.0 : 0.0;//二分类标签:1为上涨,0为下跌
//KNN核心算法:K最近邻预测函数
knn_predict() =>
var historical_features = array.new<array<float>>();//历史特征向量存储
var historical_labels = array.new<float>();//历史标签存储
//当前样本特征向量:使用已知的历史数据避免未来函数
current_features = array.from<float>(
price_momentum[1], rsi_feature[1], volume_ratio[1], volatility[1],
trend_strength[1], macd_feature[1], bb_position[1]
)
//历史数据收集:维护固定大小的历史样本窗口
if bar_index >= lookback
//滑动窗口:移除最旧数据
if array.size(historical_features) >= lookback
array.shift(historical_features)
array.shift(historical_labels)
//添加新的历史样本:使用[2]期特征预测[1]期走势
hist_features = array.from<float>(
price_momentum[2], rsi_feature[2], volume_ratio[2], volatility[2],
trend_strength[2], macd_feature[2], bb_position[2]
)
array.push(historical_features, hist_features)
array.push(historical_labels, target_label[1])
//KNN预测计算:基于历史相似样本进行预测
prediction = 0.0
if array.size(historical_features) >= k
distances = array.new<float>();//距离存储数组
labels = array.new<float>();//对应标签存储数组
//距离计算:计算当前样本与所有历史样本的欧几里得距离
for i = 0 to array.size(historical_features) - 1
hist_point = array.get(historical_features, i)
distance = 0.0
//欧几里得距离计算:各维度差值平方和的平方根
for j = 0 to array.size(current_features) - 1
curr_val = array.get(current_features, j)
hist_val = array.get(hist_point, j)
distance += math.pow(curr_val - hist_val, 2)
distance := math.sqrt(distance)
array.push(distances, distance)
array.push(labels, array.get(historical_labels, i))
//K个最近邻选择:找出距离最小的K个样本
knn_predictions = array.new<float>();//K个最近邻的标签
knn_distances = array.new<float>();//K个最近邻的距离
for n = 0 to k - 1
min_dist = 999999.0
min_index = 0
//寻找最小距离的样本索引
for i = 0 to array.size(distances) - 1
if array.get(distances, i) < min_dist
min_dist := array.get(distances, i)
min_index := i
//保存K最近邻结果
array.push(knn_predictions, array.get(labels, min_index))
array.push(knn_distances, min_dist)
//标记已使用的样本,避免重复选择
array.set(distances, min_index, 999999.0)
//加权预测:距离越近的样本权重越大
weighted_sum = 0.0
weight_total = 0.0
for i = 0 to array.size(knn_predictions) - 1
distance = array.get(knn_distances, i)
weight = distance > 0 ? 1.0 / (distance + 0.001) : 1000.0;//反距离权重
weighted_sum += array.get(knn_predictions, i) * weight
weight_total += weight
prediction := weight_total > 0 ? weighted_sum / weight_total : 0.5
prediction
//获取KNN预测结果:执行预测算法
knn_prediction = knn_predict()
//交易信号生成:基于预测结果生成买卖信号
long_threshold = prediction_threshold;//多头信号阈值
short_threshold = 1 - prediction_threshold;//空头信号阈值
//交易信号判断:预测概率超过阈值时产生信号
long_signal = knn_prediction > long_threshold;//多头信号:预测上涨概率高
short_signal = knn_prediction < short_threshold;//空头信号:预测下跌概率高
//风险管理:计算止损止盈价格
long_stop_loss = strategy.position_avg_price * (1 - stop_loss_pct / 100);//多头止损价
long_take_profit = strategy.position_avg_price * (1 + take_profit_pct / 100);//多头止盈价
short_stop_loss = strategy.position_avg_price * (1 + stop_loss_pct / 100);//空头止损价
short_take_profit = strategy.position_avg_price * (1 - take_profit_pct / 100);//空头止盈价
//策略执行:开仓和平仓逻辑
//开仓条件:无持仓时根据信号开仓
if long_signal and strategy.position_size == 0
strategy.entry("Long", strategy.long, comment="KNN多头: " + str.tostring(knn_prediction, "#.##"))
if short_signal and strategy.position_size == 0
strategy.entry("Short", strategy.short, comment="KNN空头: " + str.tostring(knn_prediction, "#.##"))
//平仓条件:持仓时设置止损止盈
if strategy.position_size > 0
strategy.exit("Long Exit", "Long", stop=long_stop_loss, limit=long_take_profit)
if strategy.position_size < 0
strategy.exit("Short Exit", "Short", stop=short_stop_loss, limit=short_take_profit)
//图表可视化:在图表上显示预测结果和信号
plot(knn_prediction, "KNN预测", color=color.blue, linewidth=2, overlay = false);//预测值曲线
hline(prediction_threshold, "多头阈值", color=color.green, linestyle=hline.style_dashed, overlay = false);//多头阈值线
hline(1 - prediction_threshold, "空头阈值", color=color.red, linestyle=hline.style_dashed, overlay = false);//空头阈值线
hline(0.5, "中性线", color=color.gray, linestyle=hline.style_dotted, overlay = false);//中性参考线
//交易信号标记:在图表上标注买卖点
plotshape(long_signal, "多头信号", shape.triangleup, location.belowbar,
color=color.green, size=size.small);//多头信号标记
plotshape(short_signal, "空头信号", shape.triangledown, location.abovebar,
color=color.red, size=size.small);//空头信号标记
//交易提醒设置:配置交易信号的自动提醒
alertcondition(long_signal, title="KNN多头信号",
message="KNN预测多头信号,预测值: {{plot(\"KNN预测\")}}");//多头信号提醒
alertcondition(short_signal, title="KNN空头信号",
message="KNN预测空头信号,预测值: {{plot(\"KNN预测\")}}");//空头信号提醒